NodeMCU(ESP8266)定时WiFi发送数据时TCP连接复位问题求助
先来说说你遇到的两个核心问题,一步步给你拆解清楚:
一、为什么Ticker回调里无法获取有效WiFiClient?
你猜的方向有道理,但本质原因更关键:ESP8266的Ticker回调是运行在中断上下文里的,而WiFi相关的API(包括server.available())绝对不能在中断中调用。
ESP8266的WiFi栈依赖FreeRTOS的任务调度、信号量等底层机制,中断上下文里没有这些运行环境,强行调用WiFi API会导致资源锁死、数据混乱,甚至直接触发系统崩溃。这就是为什么你的串口只打印40ms_Data A——server.available()在中断里根本无法正常工作,永远拿不到有效的客户端对象。
你后来改用Ticker设置标志位、在loop()里处理WiFi连接的思路是完全正确的,这才是符合ESP8266运行机制的正确做法。
二、为什么发送几条数据后TCP连接被复位?
你现在的代码能运行,但出现TCP复位,主要有这几个潜在原因:
1. 未处理客户端输入,导致缓冲区溢出
TCP连接是双向的,你的笔记本作为客户端,可能会发送数据(比如HTTP请求的头部信息),如果你的代码一直不读取这些数据,客户端的发送缓冲区会被填满,最终客户端会主动断开连接,或者ESP的WiFi栈因为接收缓冲区溢出而发送RST包。
2. delay(1)仍会抢占WiFi栈的后台处理时间
虽然你加了delay(1),但loop()里的while(client.connected())循环还是会持续占用CPU。ESP8266的WiFi栈需要定期处理TCP的ACK确认、心跳包等后台任务,如果CPU被长时间占用,WiFi栈无法及时处理这些事务,就会被判定为连接超时,触发复位。
3. 缺少连接超时处理逻辑
你定义了CLIENT_TIMEOUT但没实际使用,当客户端长时间没有数据交互时,ESP的WiFi栈可能会自动判定连接失效,主动发送RST包断开连接。
修复方案
给你修改后的代码,针对性解决这些问题:
#include <ESP8266WiFi.h> #include <Ticker.h> /* Put your SSID & Password */ const char* ssid = "NodeMCU"; const char* password = "12345678"; /* Put IP Address details */ IPAddress local_ip(192,168,1,1); IPAddress gateway(192,168,1,1); IPAddress subnet(255,255,255,0); WiFiServer server(80); const unsigned long CLIENT_TIMEOUT = 5000; // 5秒连接超时 Ticker Data_Rec; bool Sending_40ms_Start_Flag = false; void setup() { Serial.begin(115200); pinMode(D0, OUTPUT); WiFi.softAP(ssid, password); WiFi.softAPConfig(local_ip, gateway, subnet); delay(100); server.begin(); Serial.println("HTTP server started"); Data_Rec.attach_ms(500, flag_enable); // 测试用500ms触发 } void flag_enable(){ // 这里读取FPGA数据的逻辑 // ... Sending_40ms_Start_Flag = true; } void loop(){ WiFiClient client = server.available(); if (client) { Serial.println("New client connected"); unsigned long lastActivity = millis(); // 记录客户端最后活动时间 while(client.connected()){ // 先处理客户端发来的数据,避免缓冲区溢出 while(client.available()){ char c = client.read(); // 读掉所有输入数据 // 如果需要处理客户端命令,可以在这里添加逻辑 } // 检查超时:超过5秒无活动则断开连接 if(millis() - lastActivity > CLIENT_TIMEOUT){ Serial.println("Client timeout, disconnecting"); break; } // 定时发送数据 if (Sending_40ms_Start_Flag){ client.println("Server listening.\r"); Sending_40ms_Start_Flag = false; lastActivity = millis(); // 更新最后活动时间,避免误判超时 } // 主动让出CPU,给WiFi栈处理后台任务(替代delay,非阻塞) yield(); } // 主动断开客户端连接 client.stop(); Serial.println("Client disconnected"); } }
关键修改点说明:
- 读取客户端输入:添加
while(client.available())循环,把客户端发来的数据全部读掉,彻底避免缓冲区溢出问题。 - 非阻塞超时处理:用
millis()代替delay(),记录最后活动时间,超时后主动断开连接,避免系统自动复位。 - 使用
yield():代替delay(1),主动让出CPU给WiFi栈处理后台任务,确保TCP连接的ACK、心跳包等事务能及时处理。 - 更新活动时间:每次发送数据后更新
lastActivity,避免系统误判连接超时。
这样修改后,TCP连接应该能稳定保持,不会再出现频繁复位的问题。
内容的提问来源于stack exchange,提问作者javad

