STM32 进阶封神之路(二十六):ESP8266 实战全攻略 ------TCP 通信 + 数据上传 + 远程控制 + 透传模式(库函数 + 代码落地)
上一篇我们吃透了 ESP8266 的底层原理、AT 指令集和 STM32 硬件连接,这一篇聚焦实战落地 ------ 基于 STM32F103+ESP8266,实现 "TCP 客户端连接、温湿度数据上传、手机远程控制 LED、透传模式通信" 四大核心功能,所有代码基于 AT 指令开发,可直接编译运行,覆盖物联网设备的核心应用场景!
本文结合实战场景,从 TCP 连接建立、传感器数据上传、远程控制指令解析,到透传模式配置,手把手带你搭建完整的 WiFi 通信系统,让你真正掌握 ESP8266 的工程应用!
一、实战准备:硬件环境与核心需求
1. 硬件清单
- 主控:STM32F103C8T6 最小系统板;
- 无线模块:ESP8266-12F 模块(已刷 AT 指令固件);
- 传感器:DHT11 温湿度传感器(单总线);
- 外设:LED(PB0,远程控制)、1KΩ 限流电阻、4.7KΩ 上拉电阻(DHT11);
- 辅助硬件:3.3V 电源模块(ESP8266 供电)、USB-TTL 模块(调试)、杜邦线、面包板;
- 服务器:电脑(运行网络调试助手,模拟 TCP 服务器)或手机(TCP 客户端 APP)。
2. 核心实战需求
- 功能 1:TCP 客户端连接→ESP8266 作为 TCP 客户端,连接电脑网络调试助手的 TCP 服务器;
- 功能 2:数据上传→STM32 采集 DHT11 温湿度数据,通过 ESP8266 上传到 TCP 服务器;
- 功能 3:远程控制→TCP 服务器(电脑 / 手机)发送控制指令(如 "LED_ON""LED_OFF"),STM32 解析后控制 LED;
- 功能 4:透传模式→配置 ESP8266 为透传模式,实现 STM32 与 TCP 服务器的双向透明通信(无需 AT 指令,直接收发数据)。
3. 关键硬件连接
(1)核心连接表(STM32 ↔ ESP8266 ↔ 其他模块)
表格
| 模块 | 引脚 | STM32 引脚 | 连接说明 |
|---|---|---|---|
| ESP8266 | VCC | 3.3V(外部电源) | 3.3V 供电,避免 STM32 直接供电(电流不足) |
| ESP8266 | GND | GND | 共地(STM32、ESP8266、DHT11 必须共地) |
| ESP8266 | TX | PA10(USART1_RX) | 交叉连接,ESP8266→STM32 数据传输 |
| ESP8266 | RX | PA9(USART1_TX) | 交叉连接,STM32→ESP8266 数据传输 |
| ESP8266 | CH_PD | 3.3V | 高电平使能模块,不可悬空 |
| DHT11 | VCC | 3.3V | 传感器供电 |
| DHT11 | GND | GND | 共地 |
| DHT11 | DATA | PB1 | 单总线数据引脚,外接 4.7KΩ 上拉电阻 |
| LED | 正极(串 1KΩ 电阻) | PB0 | 推挽输出,低电平点亮 |
| LED | 负极 | GND |
(2)网络拓扑
plaintext
STM32 + ESP8266(TCP客户端) ↔ WiFi路由器 ↔ 电脑(TCP服务器,网络调试助手)
- 关键前提:ESP8266 和电脑需连接同一 WiFi 路由器,确保局域网通信;
- 电脑 IP 查询:通过 "ipconfig" 查询本地 IP(如 192.168.1.105),TCP 服务器端口设为 8080。
二、核心代码实现:四大功能全流程
1. 头文件与全局变量定义
c
运行
#include "stm32f10x.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// 串口1(与ESP8266通信)配置
#define USART1_RECV_BUF_LEN 512
uint8_t USART1_Recv_Buf[USART1_RECV_BUF_LEN];
uint16_t USART1_Recv_Cnt = 0;
uint8_t USART1_Recv_Idle = 0; // 接收完成标志位
// DHT11相关定义
typedef struct {
uint8_t humidity_int; // 湿度整数部分
uint8_t humidity_dec; // 湿度小数部分(DHT11恒为0)
uint8_t temp_int; // 温度整数部分
uint8_t temp_dec; // 温度小数部分
uint8_t data_valid; // 数据有效性标志(1=有效)
} DHT11_Data_TypeDef;
DHT11_Data_TypeDef dht11_data;
// TCP服务器配置(替换为你的电脑IP和端口)
#define TCP_SERVER_IP "192.168.1.105"
#define TCP_SERVER_PORT 8080
// 控制指令定义
#define LED_ON_CMD "LED_ON"
#define LED_OFF_CMD "LED_OFF"
// 延时函数声明
void Delay_ms(uint32_t ms);
void DHT11_DelayUs(uint32_t us);
// 串口1函数声明
void USART1_Init_ESP8266(void);
void USART1_SendByte(uint8_t data);
void USART1_SendString(uint8_t *str);
uint8_t ESP8266_SendCmd(uint8_t *cmd, uint8_t *resp, uint32_t timeout);
uint8_t ESP8266_Init_STA(uint8_t *ssid, uint8_t *pwd);
uint8_t ESP8266_Connect_TCP(uint8_t *ip, uint16_t port);
void ESP8266_Enter_TransparentMode(void);
// DHT11函数声明
void DHT11_Init(void);
uint8_t DHT11_ReadData(DHT11_Data_TypeDef *data);
// 外设函数声明
void LED_Init(void);
void LED_On(void);
void LED_Off(void);
void Cmd_Parse(uint8_t *cmd_buf);
2. 基础模块初始化(串口 1+LED+DHT11)
(1)串口 1 初始化(与 ESP8266 通信)
c
运行
// 串口1发送字节
void USART1_SendByte(uint8_t data) {
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
USART_SendData(USART1, data);
}
// 串口1发送字符串
void USART1_SendString(uint8_t *str) {
while(*str != '\0') {
USART1_SendByte(*str);
str++;
}
}
// printf重定向
int fputc(int ch, FILE *f) {
USART1_SendByte((uint8_t)ch);
return ch;
}
// 串口1中断服务函数(接收ESP8266数据)
void USART1_IRQHandler(void) {
uint8_t recv_data;
// 接收非空中断(字节接收)
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) {
recv_data = (uint8_t)USART_ReceiveData(USART1);
if(USART1_Recv_Cnt < USART1_RECV_BUF_LEN - 1) {
USART1_Recv_Buf[USART1_Recv_Cnt++] = recv_data;
}
USART_ClearITPendingBit(USART1, USART_IT_RXNE);
}
// 空闲中断(数据接收完成)
if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET) {
USART1_Recv_Buf[USART1_Recv_Cnt] = '\0';
USART1_Recv_Idle = 1;
// 清除空闲中断标志
recv_data = (uint8_t)USART_ReceiveData(USART1);
(void)recv_data;
}
}
// 串口1初始化(115200bps,8N1,中断接收)
void USART1_Init_ESP8266(void) {
GPIO_InitTypeDef GPIO_InitStruct;
USART_InitTypeDef USART_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE);
// PA9(TX)复用推挽输出
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
// PA10(RX)浮空输入
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStruct);
// 串口参数配置
USART_InitStruct.USART_BaudRate = 115200;
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
USART_InitStruct.USART_StopBits = USART_StopBits_1;
USART_InitStruct.USART_Parity = USART_Parity_No;
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
USART_Init(USART1, &USART_InitStruct);
// 使能接收中断和空闲中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
// 配置NVIC优先级
NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
USART_Cmd(USART1, ENABLE);
}
// 清空串口1接收缓冲区
void USART1_Clear_Recv_Buf(void) {
memset(USART1_Recv_Buf, 0, USART1_RECV_BUF_LEN);
USART1_Recv_Cnt = 0;
USART1_Recv_Idle = 0;
}
(2)LED 初始化
c
运行
void LED_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStruct);
GPIO_SetBits(GPIOB, GPIO_Pin_0); // 初始熄灭
}
void LED_On(void) {
GPIO_ResetBits(GPIOB, GPIO_Pin_0);
printf("LED已点亮\r\n");
}
void LED_Off(void) {
GPIO_SetBits(GPIOB, GPIO_Pin_0);
printf("LED已熄灭\r\n");
}
(3)DHT11 初始化与数据采集
c
运行
// DHT11 GPIO模式切换
void DHT11_SetOutputMode(void) {
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOB, &GPIO_InitStruct);
}
void DHT11_SetInputMode(void) {
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOB, &GPIO_InitStruct);
}
// 微秒级延时
void DHT11_DelayUs(uint32_t us) {
uint32_t i;
for(i = 0; i < us * 9; i++); // 72MHz主频下校准
}
// 毫秒级延时
void Delay_ms(uint32_t ms) {
uint32_t i, j;
for(i = 0; i < ms; i++) {
for(j = 0; j < 1000; j++);
}
}
// DHT11发送起始信号
uint8_t DHT11_SendStart(void) {
DHT11_SetOutputMode();
GPIO_ResetBits(GPIOB, GPIO_Pin_1);
Delay_ms(20); // 拉低≥18ms
GPIO_SetBits(GPIOB, GPIO_Pin_1);
DHT11_DelayUs(30); // 拉高20~40us
DHT11_SetInputMode();
// 等待DHT11响应
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0) {
// 检测响应低电平
while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0);
// 检测响应高电平
while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 1);
return 1; // 响应成功
}
return 0; // 响应失败
}
// DHT11读取1位数据
uint8_t DHT11_ReadBit(void) {
uint8_t bit_val = 0;
// 低电平起始(50us)
while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0);
// 高电平持续时间决定数据(0:26~28us,1:70us)
DHT11_DelayUs(40);
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 1) {
bit_val = 1;
while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 1);
}
return bit_val;
}
// DHT11读取1字节数据
uint8_t DHT11_ReadByte(void) {
uint8_t byte_val = 0;
for(uint8_t i = 0; i < 8; i++) {
byte_val <<= 1;
byte_val |= DHT11_ReadBit();
}
return byte_val;
}
// DHT11读取温湿度数据
uint8_t DHT11_ReadData(DHT11_Data_TypeDef *data) {
uint8_t humidity_int, humidity_dec, temp_int, temp_dec, check_sum;
if(DHT11_SendStart() == 0) {
data->data_valid = 0;
return 0;
}
// 读取40位数据
humidity_int = DHT11_ReadByte();
humidity_dec = DHT11_ReadByte();
temp_int = DHT11_ReadByte();
temp_dec = DHT11_ReadByte();
check_sum = DHT11_ReadByte();
// 校验数据
if(check_sum == (humidity_int + humidity_dec + temp_int + temp_dec)) {
data->humidity_int = humidity_int;
data->humidity_dec = humidity_dec;
data->temp_int = temp_int;
data->temp_dec = temp_dec;
data->data_valid = 1;
return 1;
} else {
data->data_valid = 0;
return 0;
}
}
3. ESP8266 核心功能实现(TCP 连接 + 数据上传 + 透传)
(1)AT 指令发送与响应判断
c
运行
uint8_t ESP8266_SendCmd(uint8_t *cmd, uint8_t *resp, uint32_t timeout) {
USART1_Clear_Recv_Buf();
USART1_SendString(cmd);
USART1_SendByte('\r');
USART1_SendByte('\n');
uint32_t start_time = SystemCoreClock / 1000 * timeout;
while(start_time--) {
if(USART1_Recv_Idle == 1) {
if(strstr((char*)USART1_Recv_Buf, (char*)resp) != NULL) {
printf("发送指令:%s 响应:%s\r\n", cmd, USART1_Recv_Buf);
USART1_Clear_Recv_Buf();
return 1;
} else {
printf("发送指令:%s 响应异常:%s\r\n", cmd, USART1_Recv_Buf);
USART1_Clear_Recv_Buf();
return 0;
}
}
Delay_ms(1);
}
printf("发送指令:%s 超时无响应\r\n", cmd);
USART1_Clear_Recv_Buf();
return 0;
}
(2)ESP8266 STA 模式初始化(连接 WiFi)
c
运行
uint8_t ESP8266_Init_STA(uint8_t *ssid, uint8_t *pwd) {
uint8_t ret = 0;
// 测试模块是否在线
ret = ESP8266_SendCmd((uint8_t*)"AT", (uint8_t*)"OK", 1000);
if(ret == 0) return 0;
// 重启模块
ret = ESP8266_SendCmd((uint8_t*)"AT+RST", (uint8_t*)"OK", 3000);
if(ret == 0) return 0;
Delay_ms(2000);
// 设置为STA模式
ret = ESP8266_SendCmd((uint8_t*)"AT+CWMODE=1", (uint8_t*)"OK", 1000);
if(ret == 0) return 0;
// 连接WiFi
uint8_t cwjap_cmd[64];
sprintf((char*)cwjap_cmd, "AT+CWJAP=\"%s\",\"%s\"", ssid, pwd);
ret = ESP8266_SendCmd(cwjap_cmd, (uint8_t*)"OK", 10000);
if(ret == 0) return 0;
printf("ESP8266已连接WiFi:%s\r\n", ssid);
return 1;
}
(3)TCP 客户端连接(连接电脑服务器)
c
运行
uint8_t ESP8266_Connect_TCP(uint8_t *ip, uint16_t port) {
uint8_t ret = 0;
uint8_t cipstart_cmd[64];
// 关闭之前的TCP连接
ESP8266_SendCmd((uint8_t*)"AT+CIPCLOSE", (uint8_t*)"OK", 1000);
// 建立TCP连接(格式:AT+CIPSTART="TCP","IP",端口)
sprintf((char*)cipstart_cmd, "AT+CIPSTART=\"TCP\",\"%s\",%d", ip, port);
ret = ESP8266_SendCmd(cipstart_cmd, (uint8_t*)"CONNECT", 5000);
if(ret == 0) return 0;
// 查询连接状态
ret = ESP8266_SendCmd((uint8_t*)"AT+CIPSTATUS", (uint8_t*)"STATUS:3", 1000);
if(ret == 0) return 0;
printf("TCP连接成功!服务器IP:%s,端口:%d\r\n", ip, port);
return 1;
}
(4)数据上传到 TCP 服务器
c
运行
/**
* @brief 发送数据到TCP服务器
* @param data:要发送的数据
* @param len:数据长度
* @retval 1=成功,0=失败
*/
uint8_t ESP8266_SendData_TCP(uint8_t *data, uint16_t len) {
uint8_t ret = 0;
uint8_t cipsend_cmd[32];
// 发送数据指令:AT+CIPSEND=长度
sprintf((char*)cipsend_cmd, "AT+CIPSEND=%d", len);
ret = ESP8266_SendCmd(cipsend_cmd, (uint8_t*)">", 1000);
if(ret == 0) return 0;
// 发送实际数据
USART1_Clear_Recv_Buf();
USART1_SendString(data);
Delay_ms(500); // 等待发送完成
// 检查发送结果
if(strstr((char*)USART1_Recv_Buf, "SEND OK") != NULL) {
printf("TCP数据发送成功:%s\r\n", data);
USART1_Clear_Recv_Buf();
return 1;
} else {
printf("TCP数据发送失败:%s\r\n", USART1_Recv_Buf);
USART1_Clear_Recv_Buf();
return 0;
}
}
// 温湿度数据上传(封装为JSON格式)
void DHT11_Data_Upload(void) {
if(DHT11_ReadData(&dht11_data) == 1) {
uint8_t upload_data[64];
// 封装为JSON格式,便于服务器解析
sprintf((char*)upload_data, "{\"temp\":\"%d.%d\",\"humidity\":\"%d.%d\"}",
dht11_data.temp_int, dht11_data.temp_dec,
dht11_data.humidity_int, dht11_data.humidity_dec);
ESP8266_SendData_TCP(upload_data, strlen((char*)upload_data));
} else {
printf("DHT11数据采集失败\r\n");
}
}
(5)远程控制指令解析
c
运行
void Cmd_Parse(uint8_t *cmd_buf) {
// 解析LED控制指令
if(strstr((char*)cmd_buf, LED_ON_CMD) != NULL) {
LED_On();
// 回复服务器指令执行结果
ESP8266_SendData_TCP((uint8_t*)"LED已点亮", strlen("LED已点亮"));
} else if(strstr((char*)cmd_buf, LED_OFF_CMD) != NULL) {
LED_Off();
ESP8266_SendData_TCP((uint8_t*)"LED已熄灭", strlen("LED已熄灭"));
} else {
// 未知指令
ESP8266_SendData_TCP((uint8_t*)"未知指令", strlen("未知指令"));
}
}
// 处理ESP8266接收的TCP数据
void ESP8266_RecvData_Handle(void) {
if(USART1_Recv_Idle == 1) {
// 普通模式下,TCP数据格式为"+IPD,长度:数据",需提取有效数据
uint8_t *ipd_start = strstr((char*)USART1_Recv_Buf, "+IPD,");
if(ipd_start != NULL) {
// 提取数据部分(跳过"+IPD,len:")
uint8_t *data_start = strchr(ipd_start, ':') + 1;
printf("收到TCP服务器数据:%s\r\n", data_start);
// 解析指令
Cmd_Parse(data_start);
}
USART1_Clear_Recv_Buf();
}
}
(6)透传模式配置与通信
c
运行
// 进入透传模式
void ESP8266_Enter_TransparentMode(void) {
uint8_t ret = 0;
// 1. 确保TCP连接已建立
ret = ESP8266_SendCmd((uint8_t*)"AT+CIPSTATUS", (uint8_t*)"STATUS:3", 1000);
if(ret == 0) {
printf("TCP未连接,无法进入透传模式\r\n");
return;
}
// 2. 设置为透传模式
ret = ESP8266_SendCmd((uint8_t*)"AT+CIPMODE=1", (uint8_t*)"OK", 1000);
if(ret == 0) return;
// 3. 启动透传(发送AT+CIPSEND,无参数)
ret = ESP8266_SendCmd((uint8_t*)"AT+CIPSEND", (uint8_t*)">", 1000);
if(ret == 0) return;
printf("已进入透传模式!数据将直接收发,退出请发送+++(无回车换行)\r\n");
}
// 透传模式数据收发(主循环中调用)
void ESP8266_Transparent_Comm(void) {
// 接收TCP数据并转发到串口(电脑调试)
if(USART1_Recv_Idle == 1) {
printf("透传接收:%s\r\n", USART1_Recv_Buf);
USART1_Clear_Recv_Buf();
}
// 采集DHT11数据并透传发送
if(DHT11_ReadData(&dht11_data) == 1) {
uint8_t send_data[64];
sprintf((char*)send_data, "透传数据:温度=%d.%d℃,湿度=%d.%d%%RH",
dht11_data.temp_int, dht11_data.temp_dec,
dht11_data.humidity_int, dht11_data.humidity_dec);
USART1_SendString(send_data);
printf("透传发送:%s\r\n", send_data);
Delay_ms(5000); // 每5秒发送一次
}
}
4. 主函数:整合四大功能
c
运行
int main(void) {
// 初始化系统时钟
SystemInit();
// 初始化基础模块
USART1_Init_ESP8266();
LED_Init();
printf("STM32+ESP8266 WiFi实战系统初始化成功!\r\n");
printf("=======================================\r\n\r\n");
// 1. ESP8266连接WiFi(替换为你的WiFi名称和密码)
uint8_t wifi_ssid[] = "MyWiFi";
uint8_t wifi_pwd[] = "12345678";
uint8_t esp_init_ret = ESP8266_Init_STA(wifi_ssid, wifi_pwd);
if(esp_init_ret == 0) {
printf("ESP8266 WiFi连接失败,请检查配置!\r\n");
while(1);
}
// 2. 连接TCP服务器
uint8_t tcp_connect_ret = ESP8266_Connect_TCP((uint8_t*)TCP_SERVER_IP, TCP_SERVER_PORT);
if(tcp_connect_ret == 0) {
printf("TCP连接失败,请检查服务器IP和端口!\r\n");
while(1);
}
// 选择工作模式:0=普通模式(数据上传+远程控制),1=透传模式
#define WORK_MODE 0
if(WORK_MODE == 0) {
// 普通模式:数据上传+远程控制
printf("进入普通模式:每5秒上传温湿度,支持LED_ON/LED_OFF指令控制\r\n");
while(1) {
// 每5秒上传一次温湿度数据
static uint32_t upload_cnt = 0;
if(upload_cnt++ >= 5000) {
upload_cnt = 0;
DHT11_Data_Upload();
}
// 处理远程控制指令
ESP8266_RecvData_Handle();
Delay_ms(1);
}
} else {
// 透传模式:双向透明通信
ESP8266_Enter_TransparentMode();
while(1) {
ESP8266_Transparent_Comm();
}
}
}
三、实战步骤与运行效果
1. 前期准备
(1)电脑端配置
- 安装网络调试助手(如 SSCOM、TCP&UDP 测试工具);
- 确保电脑与 ESP8266 连接同一 WiFi 路由器;
- 打开网络调试助手,选择 "TCP 服务器" 模式,设置端口为 8080,点击 "开启监听"。
(2)代码配置
- 替换代码中的
wifi_ssid和wifi_pwd为你的 WiFi 名称和密码; - 替换
TCP_SERVER_IP为你的电脑本地 IP(通过ipconfig查询); - 选择工作模式(
WORK_MODE=0为普通模式,WORK_MODE=1为透传模式)。
2. 普通模式运行效果(数据上传 + 远程控制)
(1)数据上传效果
STM32 每 5 秒采集一次 DHT11 数据,通过 ESP8266 上传到 TCP 服务器,网络调试助手接收如下:
plaintext
{"temp":"25.0","humidity":"60.0"}
{"temp":"25.1","humidity":"60.2"}
(2)远程控制效果
在网络调试助手发送指令 "LED_ON",STM32 解析后点亮 LED,同时回复:
plaintext
LED已点亮
发送指令 "LED_OFF",LED 熄灭,回复:
plaintext
LED已熄灭
3. 透传模式运行效果
配置WORK_MODE=1,系统进入透传模式,网络调试助手与 STM32 可直接收发数据:
- STM32 每 5 秒发送透传数据:
透传数据:温度=25.0℃,湿度=60.0%RH; - 网络调试助手发送 "Hello ESP8266",STM32 串口打印:
透传接收:Hello ESP8266; - 无需 AT 指令,数据直接透明传输,适合高频数据通信。
四、ESP8266 实战避坑指南(10 + 高频错误)
1. TCP 连接失败(返回 CONNECT FAIL)
- 原因 1:ESP8266 与电脑未连接同一 WiFi 路由器;解决:确保两者在同一局域网,重新检查 WiFi 名称和密码;
- 原因 2:电脑防火墙阻止了 TCP 端口(如 8080);解决:关闭电脑防火墙,或在防火墙中放行 8080 端口;
- 原因 3:服务器 IP 或端口错误;解决:通过
ipconfig重新查询电脑 IP,确保端口与网络调试助手一致; - 原因 4:ESP8266 已建立其他 TCP 连接;解决:连接前发送
AT+CIPCLOSE关闭现有连接。
2. 数据发送失败(返回 ERROR 或无响应)
- 原因 1:未建立 TCP 连接;解决:先调用
ESP8266_Connect_TCP建立连接,再发送数据; - 原因 2:发送数据长度超过 ESP8266 限制(单次最大 1460 字节);解决:分多次发送,每次长度≤1460 字节;
- 原因 3:发送数据前未等待
>提示符;解决:发送AT+CIPSEND=len后,必须等待 ESP8266 返回>,再发送实际数据。
3. 接收不到 TCP 服务器数据
- 原因 1:普通模式下未解析
+IPD格式数据;解决:在ESP8266_RecvData_Handle中提取+IPD,len:后的有效数据; - 原因 2:透传模式下未启动透传(未发送
AT+CIPSEND);解决:进入透传模式后,必须发送AT+CIPSEND(无参数),等待>后再通信; - 原因 3:串口接收缓冲区溢出;解决:增大
USART1_RECV_BUF_LEN(如从 256 改为 512 字节)。
4. 透传模式无法退出
- 原因:退出透传模式需发送
+++(无回车换行),且发送间隔≥1 秒;解决:在网络调试助手发送+++(不勾选 "自动回车"),等待 ESP8266 返回OK,即可退出透传模式。
5. DHT11 数据采集失败
- 原因 1:DHT11 DATA 引脚未接 4.7KΩ 上拉电阻;解决:添加 4.7KΩ 上拉电阻,确保数据传输稳定;
- 原因 2:延时函数不精准(导致起始信号或数据读取错误);解决:根据 STM32 主频校准
DHT11_DelayUs函数。
五、总结:ESP8266 实战核心要点与进阶方向
1. 核心要点回顾
- ESP8266 实战核心流程:硬件连接→WiFi 连接→TCP 连接→数据收发 / 透传;
- 普通模式:适合低频率数据上传和控制,需通过
AT+CIPSEND发送数据,解析+IPD格式接收数据; - 透传模式:适合高频数据通信,无需 AT 指令,直接透明传输,退出需发送
+++; - 避坑核心:供电充足、WiFi 同网、TCP 连接前置、数据格式解析正确。
2. 进阶学习方向
- 云平台对接:将数据上传到阿里云、腾讯云或 OneNet,实现远程监控;
- MQTT 协议通信:基于 MQTT 协议(轻量级物联网协议),实现低功耗、可靠的数据传输;
- 手机 APP 开发:通过 Android/iOS APP 作为 TCP 客户端,实现手机远程控制;
- 多设备通信:配置 ESP8266 为 AP 模式,实现多个 STM32 设备的局域网通信;
- 低功耗优化:ESP8266 进入休眠模式,定时唤醒上传数据,延长电池续航。
掌握 ESP8266 实战后,你已具备物联网设备的无线通信能力,可应用于智能家居、远程监控、数据记录仪等场景。下一篇我们将学习 STM32 的 CAN 总线通信,实现工业级设备间的高可靠性数据传输!