Arduino与PC间TCP/UDP传感器数据传输消息间隔不稳定问题
问题:Arduino传感器数据网络传输时消息间隔不稳定
我尝试将Arduino(作为服务器)的传感器数据通过网络实时流式传输到电脑(客户端)。传感器数据每秒生成速率稳定,但消息到达客户端的时间间隔极不规则。
我测试了TCP和UDP的单向、双向通信极简示例来定位问题,还尝试过添加延迟、调整TCP_NODELAY等设置、减小消息大小、添加消息头等方式同步连接,但所有场景下,消息都以不稳定的突发形式到达:

消息内容固定为"test",打印帧号用于演示
请问该现象的原因是什么?如何让消息间隔更稳定?
问题原因分析
- Arduino WiFi模块的底层缓冲机制:WiFiNINA模块会对小数据包做缓冲,为了提升传输效率,会攒够一定数据量或等待超时后才批量发送,导致客户端收到突发数据。
- TCP Nagle算法的残留影响:即使客户端开启
TCP_NODELAY,Arduino端的WiFiServer实现可能未同步禁用该算法,或模块底层仍会合并数据;此外TCP的滑动窗口、ACK机制也可能延迟数据发送。 delay()函数的精度误差:Arduino的delay()是阻塞式的,会被WiFi模块的任务调度、网络栈操作打断,导致实际发送间隔偏离预期。- UDP的无线传输优化:UDP虽为无连接协议,但WiFi模块为减少无线帧开销,会合并小UDP数据包后发送,尤其在低带宽或高干扰环境下更明显。
解决方案
1. 强制TCP立即发送数据
在Arduino的TCP服务器代码中,每次调用write()后添加client.flush(),强制模块立即发送缓冲数据:
void loop() { WiFiClient client = server.available(); if (client.connected()) { client.write("test"); client.flush(); // 强制立即发送 delay(16); } }
2. 用非阻塞定时替代delay()
改用millis()实现非阻塞定时,避免网络操作打断发送间隔:
unsigned long previousMillis = 0; const long sendInterval = 16; void loop() { unsigned long currentMillis = millis(); if (currentMillis - previousMillis >= sendInterval) { previousMillis = currentMillis; WiFiClient client = server.available(); if (client.connected()) { client.write("test"); client.flush(); } } }
3. UDP数据包填充避免合并
将UDP数据包填充到无线帧最小长度(通常64字节),让模块无法合并数据包:
void loop() { Udp.beginPacket(Udp.remoteIP(), Udp.remotePort()); Udp.write("test"); // 填充到64字节 for(int i=4; i<64; i++){ Udp.write(0); } Udp.endPacket(); // 非阻塞延迟 unsigned long currentMillis = millis(); while(millis() - currentMillis < 16); }
4. 客户端时间戳校准
在Arduino发送的数据包中附加生成时间戳,客户端根据时间戳对齐数据,而非依赖接收时间:
- Arduino端:
Udp.write("test," + String(millis())); - 客户端:解析时间戳,计算数据实际生成间隔并做平滑处理。
测试用极简示例(优化版)
单向通信(服务器 -> 客户端)
TCP客户端
import time import socket ADDRESS = "xxx.xxx.xxx.xxx" PORT = 80 SIZE = 4 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) sock.connect((ADDRESS, PORT)) print("Waiting for connection...") sock.recv(SIZE) print("Connected") t0 = 0 f = 0 while True: data = sock.recv(SIZE).decode() if f == 0: t0 = time.time() time.sleep(.0001) f += 1 fps = int(f / (time.time() - t0)) print(f"\r{f} FPS: {fps} data: {data} ", end='')
TCP服务器
#include <WiFiNINA.h> WiFiServer server(80); unsigned long previousMillis = 0; const long sendInterval = 16; void setup() { Serial.begin(115200); int status = WL_IDLE_STATUS; while (status != WL_CONNECTED) { status = WiFi.begin("ssid", "password"); delay(1000); } server.begin(); Serial.println("connected"); Serial.print("SSID: "); Serial.println(WiFi.SSID()); IPAddress ip = WiFi.localIP(); Serial.println(ip); } void loop() { unsigned long currentMillis = millis(); if (currentMillis - previousMillis >= sendInterval) { previousMillis = currentMillis; WiFiClient client = server.available(); if (client.connected()) { client.write("test"); client.flush(); } } }
UDP客户端
import socket import time ADDRESS = "xxx.xxx.xxx.xxx" PORT = 2390 SIZE = 64 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.sendto(b"s", (ADDRESS, PORT)) print("Waiting for connection...") sock.recvfrom(SIZE) print("Connected") t0 = 0 f = 0 while True: data, addr = sock.recvfrom(SIZE) data = data.decode().split('\0')[0] if f == 0: t0 = time.time() time.sleep(.0001) f += 1 fps = int(f / (time.time() - t0)) print(f"\r{f} FPS: {fps} data: {data} ", end='')
UDP服务器
#include <WiFiNINA.h> #include <WiFiUdp.h> WiFiUDP Udp; unsigned long previousMillis = 0; const long sendInterval = 16; void setup() { Serial.begin(115200); int status = WL_IDLE_STATUS; while (status != WL_CONNECTED) { status = WiFi.begin("ssid", "password"); delay(1000); } Udp.begin(2390); Serial.println("connected"); Serial.print("SSID: "); Serial.println(WiFi.SSID()); IPAddress ip = WiFi.localIP(); Serial.println(ip); } void loop() { unsigned long currentMillis = millis(); if (currentMillis - previousMillis >= sendInterval) { previousMillis = currentMillis; if (Udp.remoteIP() != IPAddress(0,0,0,0)) { Udp.beginPacket(Udp.remoteIP(), Udp.remotePort()); Udp.write("test"); for(int i=4; i<64; i++){ Udp.write(0); } Udp.endPacket(); } } }
双向通信(服务器 <-> 客户端)
TCP客户端
import time import socket ADDRESS = "xxx.xxx.xxx.xxx" PORT = 80 SIZE = 4 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) sock.connect((ADDRESS, PORT)) print("Waiting for connection...") sock.send(b's') sock.recv(SIZE) print("Connected") t0 = 0 f = 0 while True: sock.send(b's') data = sock.recv(SIZE).decode() if f == 0: t0 = time.time() time.sleep(.0001) f += 1 fps = int(f / (time.time() - t0)) print(f"\r{f} FPS: {fps} data: {data} ", end='')
TCP服务器
#include <SPI.h> #include <WiFiNINA.h> WiFiServer server(80); boolean alreadyConnected = false; void setup() { Serial.begin(9600); while (!Serial); Serial.print("Connecting to Wifi... "); int status = WL_IDLE_STATUS; while (status != WL_CONNECTED) { status = WiFi.begin("ssid", "password"); delay(500); } Serial.println("connected."); server.begin(); Serial.print("SSID: "); Serial.println(WiFi.SSID()); IPAddress ip = WiFi.localIP(); Serial.println(ip); } void loop() { WiFiClient client = server.available(); if (client) { if (!alreadyConnected) { client.flush(); alreadyConnected = true; } if (client.available() > 0) { char thisChar = client.read(); client.write("test"); client.flush(); } } }
UDP客户端
import socket import time ADDRESS = "xxx.xxx.xxx.xxx" PORT = 2390 SIZE = 64 sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.sendto(b"s", (ADDRESS, PORT)) print("Waiting for connection...") sock.recvfrom(SIZE) print("Connected") t0 = 0 f = 0 while True: sock.sendto(b"s", (ADDRESS, PORT)) data, addr = sock.recvfrom(SIZE) data = data.decode().split('\0')[0] if f == 0: t0 = time.time() time.sleep(.0001) f += 1 fps = int(f / (time.time() - t0)) print(f"\r{f} FPS: {fps} data: {data} ", end='')
UDP服务器
#include <WiFiNINA.h> #include <WiFiUdp.h> WiFiUDP Udp; void setup() { Serial.begin(115200); while (!Serial); Serial.print("Connecting to Wifi... "); int status = WL_IDLE_STATUS; while (status != WL_CONNECTED) { status = WiFi.begin("wifi", "password"); delay(500); } Serial.println("connected."); Udp.begin(2390); Serial.print("SSID: "); Serial.println(WiFi.SSID()); IPAddress ip = WiFi.localIP(); Serial.println(ip); } void loop() { if (!Udp.parsePacket()) { return; } while (Udp.read() != -1); Udp.beginPacket(Udp.remoteIP(), Udp.remotePort()); Udp.write("test"); for(int i=4; i<64; i++){ Udp.write(0); } Udp.endPacket(); }
内容的提问来源于stack exchange,提问作者dandev
相关产品推荐
相关产品推荐

