☰
STC12C5A60S2 与 ESP8266 联动开发学习记录
2026/10/9 18:54:31 网站建设 项目流程

0. 前言

最近在做一个基于 STC12 单片机的小项目,需要把传感器数据通过 Wi-Fi 发送到上位机。我选择了最常见的 ESP8266 模块作为无线通信方案。考虑到现场没有路由器,而且只需要一对一数据传输,我决定把 ESP8266 配置成AP 模式 + TCP Server,这样电脑或手机直接连接 ESP8266 发出的热点,就能收到数据了。

这篇文章主要记录我在开发过程中对esp8266.c这个模块的封装和调试过程,方便以后回顾。整体代码基于 STC12C5A60S2,使用 UART1 与 ESP8266 通信。文末也会总结一些踩过的坑,希望对有同样需求的朋友有帮助。


1. 系统整体结构

整个系统由 STC12 单片机作为主控制器,外接多个传感器:

  • 超声波测距模块(用于检测钻头位置,统计打孔次数)
  • 声音传感器 + 单片机 ADC(用于估算环境分贝)
  • HX711 称重传感器(测量压力)
  • OLED 显示屏(本地显示数据)
  • ESP8266 Wi-Fi 模块(无线发送数据到上位机)
while(1) { Get_Weight(); // 读取 HX711 重量 adc_raw = GetADCResult(ADC_CH1)*100; adc_smooth = ADC_LowPass(adc_raw); // 低通滤波 dkcs(); // 超声波检测打孔动作并计数 ESP8266_SendHexPacket(distance, sound_db, drill_count, pressure); }

可以看到,esp8266.c中的ESP8266_SendHexPacket函数负责将 4 个数据打包成二进制帧并发送出去。这就是本文的重点。


2. ESP8266 的 AT 指令配置流程

在写单片机程序之前,我习惯先用 USB 转串口模块连接 ESP8266,手动发送 AT 指令验证模块是否正常工作。我使用 9600 波特率(和单片机程序一致),发送以下命令:

命令作用
AT串口通信测试,返回 OK
AT+CWMODE=2设置 Wi-Fi 模式为 AP(热点)
AT+CWSAP="Drill_AP","12345678",5,3创建热点:SSID 为 Drill_AP,密码 12345678,通道 5,加密方式 WPA2_PSK
AT+CIPMUX=1开启多连接模式(TCP Server 必须)
AT+CIPSERVER=1,8080开启 TCP 服务器,监听 8080 端口

配置完成后,电脑连接热点Drill_AP,用网络调试助手连接192.168.4.1:8080(ESP8266 默认网关 IP),就能接收到后续单片机发来的数据。

这些命令都封装在了esp8266.c的ESP8266_InitAPServer()函数中,单片机启动时自动执行一次。


3. esp8266.c 代码详解

3.1 宏定义区

/* ESP8266热点配置 */ #define ESP_AP_NAME "Drill_AP" #define ESP_AP_PASS "12345678" #define ESP_SERVER_PORT "8080"

把热点名称、密码和端口号定义为宏,方便修改。注意密码长度至少 8 位,否则 ESP8266 会拒绝创建 WPA2 加密的热点。

3.2 软件延时函数

void ESP8266_Delay_ms(unsigned int ms) { unsigned int i, j; for(i = 0; i < ms; i++) for(j = 0; j < 110; j++); }

这个函数基于 STC12 的机器周期做粗略延时。实际延时时间与单片机主频有关,我用 11.0592MHz 晶振时,内层循环 110 次大约接近 1ms。这个延时并不精确,但对 ESP8266 的 AT 指令等待来说足够用了。

3.3 AT 命令发送函数

void ESP8266_SendCommand(char *cmd, unsigned int delay_time) { UART_SendString(cmd); UART_SendString("\r\n"); ESP8266_Delay_ms(delay_time); }

ESP8266 的每条 AT 命令都需要以回车换行\r\n结尾。这个函数将命令字符串加上\r\n发送出去,然后等待一段时间让模块处理。注意这里并没有检查模块是否返回OK,是一个简化设计。

3.4 初始化 AP 模式并开启 TCP Server

void ESP8266_InitAPServer(void) { ESP8266_Delay_ms(5000); ESP8266_SendCommand("AT", 1000); ESP8266_SendCommand("AT+CWMODE=2", 1000); // 发送 AT+CWSAP="热点名","密码",通道,加密方式 UART_SendString("AT+CWSAP=\""); UART_SendString(ESP_AP_NAME); UART_SendString("\",\""); UART_SendString(ESP_AP_PASS); UART_SendString("\",5,3\r\n"); ESP8266_Delay_ms(2000); ESP8266_SendCommand("AT+CIPMUX=1", 1000); // 开启 TCP Server UART_SendString("AT+CIPSERVER=1,"); UART_SendString(ESP_SERVER_PORT); UART_SendString("\r\n"); ESP8266_Delay_ms(2000); }

这里有一点需要注意:AT+CWSAP命令的参数中包含引号,所以不能直接使用ESP8266_SendCommand发送,而是分步拼接字符串。通道我固定使用 5,加密方式 3 代表WPA2_PSK。如果不需要密码,可以改为 0(OPEN),但为了安全起见还是设置了密码。

3.5 十六进制数据帧发送函数

这是最核心的函数,负责将 4 个unsigned int类型的数据打包成 12 字节的二进制帧,并通过 ESP8266 发送给上位机。

unsigned char ESP8266_SendHexPacket(unsigned int distance, unsigned int db, unsigned int count, unsigned int pressure) { unsigned char frame[12]; unsigned char i; frame[0] = 0xAA; frame[1] = 0x55; frame[2] = distance >> 8; frame[3] = distance & 0xFF; frame[4] = db >> 8; frame[5] = db & 0xFF; frame[6] = count >> 8; frame[7] = count & 0xFF; frame[8] = pressure >> 8; frame[9] = pressure & 0xFF; frame[10] = 0x0D; frame[11] = 0x0A; // 发送 AT+CIPSEND=0,12 UART_SendString("AT+CIPSEND=0,12\r\n"); // 等待 '>' 提示 if(UART_WaitString(">", 5000) == 0) { return 0; } for(i = 0; i < 12; i++) { UART_SendByte(frame[i]); } // 等待 SEND OK if(UART_WaitString("SEND OK", 8000) == 0) { return 0; } return 1; }
帧格式设计
字节索引内容说明
00xAA帧头
10x55帧头
2distance 高字节距离数据高 8 位
3distance 低字节距离数据低 8 位
4db 高字节分贝高 8 位
5db 低字节分贝低 8 位
6count 高字节打孔次数高 8 位
7count 低字节打孔次数低 8 位
8pressure 高字节压力高 8 位
9pressure 低字节压力低 8 位
100x0D帧尾回车
110x0A帧尾换行

所有 16 位数据都采用大端模式(高字节在前)。帧头0xAA 0x55和帧尾0x0D 0x0A可以帮助接收端快速定位一帧数据。目前没有加校验和,后续可以考虑增加 XOR 或 CRC 校验。

发送流程

ESP8266 在 TCP Server 多连接模式下,发送数据需要先发送AT+CIPSEND=<连接号>,<数据长度>命令。如果连接正常,模块会返回>提示符,此时才能发送真正的数据。数据发送完成后,模块若成功发送到客户端,会返回SEND OK。

函数中使用UART_WaitString()来等待特定字符串,并设置了超时时间(等待>为 5 秒,等待SEND OK为 8 秒)。如果超时或没有收到预期回复,函数返回 0 表示发送失败;否则返回 1 表示成功。


4. 主程序中的调用与联调

在main.c中,系统启动后先执行各种初始化:

OLED_Init(); Ultrasonic_Init(); OLED_ShowFrame(); OLED_ShowData(); UART_Init(); IO_Init(); Get_Zero(); ADC_Init(ADC_PORT1); last_distance = Ultrasonic_GetDistance(); ESP8266_InitAPServer(); // 初始化 ESP8266 delay_ms(2000);

然后主循环周期性地采集数据、显示 OLED,并调用ESP8266_SendHexPacket发送。

实际测试时,电脑连接热点Drill_AP,打开网络调试助手,以 TCP Client 模式连接192.168.4.1:8080,就能看到类似下面的十六进制数据流(十六进制显示):

Text

AA 55 00 0C 00 50 00 03 00 0C 0D 0A

解析出来就是:距离 12,分贝 80,打孔次数 3,压力 12。


5. 调试过程中踩过的坑

5.1 串口波特率不一致

刚开始 ESP8266 默认波特率是 115200,而我的 STC12 程序使用 9600,导致模块完全没反应。后来用AT+UART_DEF=9600,8,1,0,0把 ESP8266 的默认波特率改成了 9600,或者也可以在初始化时发送AT+UART=9600,8,1,0,0临时修改。注意修改后需要重启模块生效。

5.2 ESP8266 供电不足

ESP8266 在发送数据时瞬间电流可能达到 300mA 以上,如果使用单片机板载的 3.3V 供电,很可能导致模块不断复位,表现为 AT 命令响应异常。解决方法:使用独立 5V 转 3.3V 模块给 ESP8266 供电,并共地。不要直接用 STC12 的 IO 口给 ESP8266 供电。

5.3 发送失败

由于ESP8266_SendHexPacket在等待>或SEND OK时用了较长的超时时间,单片机的发送速度太快,一下发送了好几条指令,ESP8266来不及接收,给单片机的串口发送加上一段延时就解决了这个问题

5.4 调试串口与 ESP8266 共用串口导致数据错乱

在main.c里有一行调试代码:

UART_SendNumber(distance);

这会把距离值通过同一个串口发送给 ESP8266,而 ESP8266 会将其当作 AT 命令的一部分,导致后续通信异常。调试信息不能和 ESP8266 共用一个串口,要么删除,要么改用其他串口输出。这是本次调试中浪费最多时间的一个坑。

5.5 ESP8266 返回数据残留

多次发送后,串口接收缓冲区可能会残留SEND OK或CLOSED等字符串,影响下一次UART_WaitString(">")的判断。建议在发送AT+CIPSEND前调用一个清空接收缓冲区的函数,或者使用时在每次发送前清空 UART 接收缓存。

5.6 单片机串口5V转3.3V

在这里值得一提的是单片机的串口电平是5V而ESP8266的串口电平是3.3V,其实很简单,只要给串口交互部分加上一个电平转换电路就能解决这个问题


6. 改进方向

目前这个版本虽然能跑通,但还有很多值得优化的地方:

  1. 增加 AT 命令响应的检测:ESP8266_InitAPServer中发送每条命令后都应该检查返回OK,否则若初始化失败,程序仍然继续运行,导致后续发送全部失败。

  2. 发送函数改为非阻塞或状态机:避免卡死主循环,提高系统实时性。

  3. 处理 TCP 客户端断开:当客户端断开时,ESP8266 会返回CLOSED,单片机没有处理,下一次发送会失败。可以检测到CLOSED后自动重启 Server 或等待新客户端连接。

  4. 增加数据校验:在数据帧中增加一个简单的 XOR 校验字节,提高可靠性。

  5. 使用定时器精确延时:替换粗糙的软件延时,提高移植性。


7. 总结

本文记录了我使用 STC12C5A60S2 驱动 ESP8266 实现 AP 模式 TCP 服务器数据透传的过程。esp8266.c模块封装了初始化配置和二进制帧发送的功能,通过 AT 指令与模块交互。虽然目前还有一些不足,但已经可以完成基本的数据传输需求。

这次开发让我对 ESP8266 的 AT 指令有了更深入的理解,也积累了不少调试经验。后续会继续优化代码,尝试加入断线重连和缓冲区管理,让系统更稳定。

如果你也在做类似的项目,希望这篇文章能给你一些参考。如果有错误或更好的实现方式,欢迎交流讨论。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询