You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ESP32 Web Server串口数据周期性更新问题求助

解决ESP32通过UART获取数据并周期性更新Web接口的问题

首先咱们先拆解下你当前代码的核心问题:

  1. readuart()函数每次只读取单个字符,既没处理串口缓冲区里可能存在的多字符数据,也没保留最新的串口数据状态——每次请求接口时,只会读取当下串口的一个字符,之后就没有后续更新了。
  2. HTTP GET请求是单次响应模式,客户端请求一次,服务器返回一次数据就结束连接,不会主动给客户端推送新数据。

下面给你两种实用的解决方案,按需选择:


方案一:客户端周期性请求(简单易上手)

这个方案适合对实时性要求不高的场景,让前端页面每隔一段时间自动重新请求接口,获取最新的串口数据。同时我们要优化串口数据的读取逻辑,确保能拿到完整的串口数据(比如读取到换行符为止)。

步骤1:优化串口读取逻辑+新增前端自动刷新页面

修改后的完整代码如下:

#include <WiFi.h>
#include "ESPAsyncWebServer.h"

AsyncWebServer server1(81);
const char* ssid ="mywifi";
const char* password ="mywifipass";

// 存储最新的完整串口数据
String latestUartData = "";

void readUart() {
  while (Serial.available() > 0) {
    char c = Serial.read();
    // 假设串口数据以换行符结尾,读取完整一行数据
    if (c == '\n') {
      // 去掉多余的换行/回车符
      latestUartData.trim();
      Serial.print("收到完整数据: ");
      Serial.println(latestUartData);
    } else {
      latestUartData += c;
    }
  }
}

void setup() {
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("正在连接WiFi..");
  }
  Serial.println("已连接WiFi网络");
  Serial.println(WiFi.localIP());

  // 提供纯文本数据接口
  server1.on("/test_data", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(200, "text/plain", latestUartData.c_str());
  });

  // 提供带自动刷新功能的HTML页面
  server1.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    String html = R"HTML(
    <!DOCTYPE html>
    <html>
    <body>
      <h1>UART实时数据</h1>
      <p id="data">等待数据...</p>
      <script>
        // 每隔1秒请求一次最新数据并更新页面
        setInterval(() => {
          fetch('/test_data')
            .then(response => response.text())
            .then(data => {
              document.getElementById('data').textContent = data;
            });
        }, 1000);
      </script>
    </body>
    </html>
    )HTML";
    request->send(200, "text/html", html);
  });

  server1.begin();
}

void loop() {
  // 循环读取串口数据,实时更新latestUartData
  readUart();
}

说明:

  • 新增latestUartData变量存储最新的完整串口数据,readUart()函数会在loop()中持续运行,读取串口缓冲区的所有字符,直到遇到换行符完成一次完整数据的读取。
  • 根路径/的HTML页面用JavaScript的setInterval每隔1秒自动请求/test_data接口,更新页面显示的内容。

方案二:使用Server-Sent Events(SSE)主动推送(更实时)

如果需要低延迟的实时更新,SSE是更好的选择——服务器会主动向客户端推送新数据,不需要客户端反复发起请求,保持长连接即可。

修改后的完整代码:

#include <WiFi.h>
#include "ESPAsyncWebServer.h"

AsyncWebServer server1(81);
const char* ssid ="mywifi";
const char* password ="mywifipass";

String latestUartData = "";
bool newDataAvailable = false; // 标记是否有新的串口数据

void readUart() {
  while (Serial.available() > 0) {
    char c = Serial.read();
    if (c == '\n') {
      latestUartData.trim();
      newDataAvailable = true; // 标记有新数据待推送
      Serial.print("收到新数据: ");
      Serial.println(latestUartData);
    } else {
      latestUartData += c;
    }
  }
}

void setup() {
  Serial.begin(115200);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("正在连接WiFi..");
  }
  Serial.println("已连接WiFi网络");
  Serial.println(WiFi.localIP());

  // SSE推送接口,保持长连接主动发数据
  server1.on("/stream", HTTP_GET, [](AsyncWebServerRequest *request){
    // 设置SSE专属响应头
    AsyncWebServerResponse *response = request->beginResponseStream("text/event-stream");
    response->addHeader("Cache-Control", "no-cache");
    response->addHeader("Connection", "keep-alive");
    request->send(response);

    // 持续监听新数据,有新数据就推送给客户端
    while (WiFi.status() == WL_CONNECTED) {
      if (newDataAvailable) {
        // SSE消息格式:data: 内容\n\n
        response->print("data: ");
        response->print(latestUartData);
        response->print("\n\n");
        response->send(); // 发送数据
        newDataAvailable = false; // 重置标记
      }
      delay(100); // 避免占用过多CPU资源
    }
  });

  // 提供SSE前端页面
  server1.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    String html = R"HTML(
    <!DOCTYPE html>
    <html>
    <body>
      <h1>UART实时数据(SSE主动推送)</h1>
      <p id="data">等待数据...</p>
      <script>
        // 建立SSE长连接
        const eventSource = new EventSource('/stream');
        // 接收服务器推送的新数据
        eventSource.onmessage = function(event) {
          document.getElementById('data').textContent = event.data;
        };
        // 处理连接异常
        eventSource.onerror = function(error) {
          console.error('SSE连接出错:', error);
          eventSource.close();
        };
      </script>
    </body>
    </html>
    )HTML";
    request->send(200, "text/html", html);
  });

  server1.begin();
}

void loop() {
  readUart();
}

说明:

  • SSE是基于HTTP的服务器主动推送技术,客户端和服务器保持长连接,当服务器有新数据时会立即推送给客户端。
  • 用newDataAvailable标记是否有新的串口数据,SSE连接建立后,服务器会持续检查这个标记,有新数据就推送给客户端。
  • 前端通过EventSource API建立SSE连接,自动接收服务器推送的数据并更新页面。

额外注意事项

  • 如果你的串口数据结束符不是\n,可以根据实际情况修改readUart()中的判断条件(比如\r或者自定义的结束字符)。
  • 确保ESP32的串口波特率和外部设备一致(当前代码是115200)。
  • 如果串口数据量很大,可以考虑增加缓冲区溢出处理逻辑,避免数据丢失。

内容的提问来源于stack exchange,提问作者Tea

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.09 17:52:29