STM32 进阶封神之路(二十六):ESP8266 实战全攻略 ——TCP 通信 + 数据上传 + 远程控制 + 透传模式(库函数 + 代码落地)

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)电脑端配置
  1. 安装网络调试助手(如 SSCOM、TCP&UDP 测试工具);
  2. 确保电脑与 ESP8266 连接同一 WiFi 路由器;
  3. 打开网络调试助手,选择 "TCP 服务器" 模式,设置端口为 8080,点击 "开启监听"。
(2)代码配置
  1. 替换代码中的wifi_ssidwifi_pwd为你的 WiFi 名称和密码;
  2. 替换TCP_SERVER_IP为你的电脑本地 IP(通过ipconfig查询);
  3. 选择工作模式(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 总线通信,实现工业级设备间的高可靠性数据传输!

相关推荐
Nice__J2 小时前
Mcu架构以及原理——7.寄存器编程与抽象
stm32·单片机·架构
嵌入式学习和实践2 小时前
当MCU遇上大模型:在单片机上实现AI对话的硬核玩法
人工智能·单片机·大模型
我不是程序猿儿2 小时前
【嵌入式】适合 STM32 初学者BootLoader 入门学习心得
linux·stm32·单片机·嵌入式硬件·学习
惶了个恐2 小时前
嵌入式硬件第五弹——ARM(1)
嵌入式硬件
线束线缆组件品替网3 小时前
Amphenol RJE1Y36610644401 CAT6A网线组件选型与替代指南
网络·人工智能·数码相机·电脑·音视频·硬件工程·游戏机
Suifqwu3 小时前
stm32进阶-启动文件
stm32·单片机·嵌入式硬件
火龙果里的芝麻4 小时前
STM32 FreeModbus 移植(最详细)
stm32·单片机·嵌入式硬件
weiyvyy4 小时前
嵌入式硬件接口开发的流程
人工智能·驱动开发·单片机·嵌入式硬件·硬件架构·硬件工程
weiyvyy4 小时前
嵌入式硬件接口开发的核心原则
驱动开发·单片机·嵌入式硬件·fpga开发·硬件架构·硬件工程