1) 先用一条公式判断:是不是已经把串口带宽打满了
串口 8N1 每个字节大约 10bit(起始+8位+停止)。
-
串口最大字节率 ≈
baud / 10 -
5ms 一包:每秒
1 / 0.005 = 200包 -
每包允许的最大字节数 ≈
(baud/10) / 200
举例:
-
115200bps :
115200/10 = 11520 B/s,11520/200 = 57.6B/包👉 如果你每次发送的字符串 + CRLF + 可能的转义/hex 显示 超过 ~57 字节,5ms 必炸(溢出/积压),50ms 就正常。
-
921600bps :
92160/200 = 460.8B/包(空间就大很多)
✅ 所以第一步:看你每次定时发送的内容长度 (很多串口助手还会额外加 \r\n),基本一算就能定位。
2) 5ms 下"网络收不到"的常见根因(按概率排序)
A. ESP32 UART 接收溢出 / 缓冲满
ESP32 来不及 uart_read_bytes(),会触发:
-
UART_FIFO_OVF -
UART_BUFFER_FULL
溢出后数据就丢了,网络端当然"收不到"。
B. 串口转网络在同一个任务里做,网络 send 阻塞把 UART 拖死
如果你是:收到 UART → 立刻 send() 到 TCP/UDP
5ms 下 send() 很可能因为 lwIP 缓冲不够/对端 ACK 慢/无线拥塞而卡住,导致 UART 事件处理线程读不动 → UART 溢出 → 丢数据。
C. 小包太多导致 Wi-Fi/TCP 开销暴涨(不是带宽而是"包速率"顶住了)
200Hz 的小包在 Wi-Fi 上开销很大,尤其 TCP 还要 ACK/重传/拥塞控制。
很多"网络调试助手"UI 也会有刷新限速,让你误以为没收到(但一般你这里更像前两种)。
3) 怎么判断"是否被堵塞/哪里堵住了"(最有效的3个观测点)
① UART 事件里统计溢出(看是不是 UART 侧堵)
ESP-IDF 用 UART event queue 的话,直接抓这两个事件并打点计数:
static uint32_t s_uart_ovf = 0;
case UART_FIFO_OVF:
case UART_BUFFER_FULL:
s_uart_ovf++;
ESP_LOGW(TAG, "UART overflow! cnt=%u", s_uart_ovf);
uart_flush_input(UART_NUM_1);
xQueueReset(uart_queue);
break;
只要 5ms 时这个计数飙升,根因就坐实了:UART 被顶爆。
② 看 UART 缓冲积压长度(看"读不过来")
size_t buffered = 0;
uart_get_buffered_data_len(UART_NUM_1, &buffered);
ESP_LOGI(TAG, "uart buffered=%u", (unsigned)buffered);
如果 buffered 长期接近你 install 的 rx buf 大小,说明处理不及时。
③ 网络 send 是否被"卡住/拒绝"(看网络侧堵)
给 send() 做两件事:测耗时 + 打 errno(最好用非阻塞)
int64_t t0 = esp_timer_get_time();
int n = send(sock, data, len, MSG_DONTWAIT);
int64_t dt = esp_timer_get_time() - t0;
if (n < 0) {
int e = errno;
ESP_LOGW(TAG, "send fail e=%d dt=%lldus", e, (long long)dt);
} else if (dt > 2000) {
ESP_LOGW(TAG, "send slow dt=%lldus len=%d", (long long)dt, len);
}
-
errno == EAGAIN/EWOULDBLOCK/ENOMEM:基本就是 lwIP 发送缓冲满(网络堵) -
dt明显变大:说明 send 在"等资源/拥塞",UART 很容易被拖死
4) 对应的解决方案(直接有效)
方案 1:提高波特率 or 降低每包长度(最快)
-
115200 下 5ms 每包尽量 < 50 字节(留余量)
-
或提升到 460800/921600(常用)
-
如果 USB 转串口支持,优先上 RTS/CTS 硬件流控
方案 2:把"串口接收"和"网络发送"解耦(根治阻塞互相拖死)
-
Task A:只负责
uart_read_bytes,放入 ringbuffer/streambuffer -
Task B:从 buffer 取数据,批量合并后再发网络(比如攒 20~50ms 或攒到 512B 再 send)
这样即使网络偶尔堵,也不会立刻把 UART 顶爆。
方案 3:增大 UART 驱动缓冲
uart_driver_install(UART_NUM_1, 8*1024, 0, 20, &uart_queue, 0);
rx buf 先拉大(比如 4K/8K),能显著提高抗抖动能力。
方案 4:合并小包再发(对 Wi-Fi/TCP 特别重要)
5ms 一次的小包,建议在设备端做聚合:例如每 20ms 打包一次发出。
5) 一个很实用的定位顺序(你照着做基本一次就定位)
-
算长度:看 5ms 每包到底多少字节、波特率多少(先排除"纯带宽不够")
-
ESP32 打开 UART 溢出计数(FIFO_OVF / BUFFER_FULL)
-
给
send()打 errno + 耗时(确认是不是网络侧把任务卡住) -
若是 TCP:优先"解耦 + 合并发送",立竿见影