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

ESP32 Arduino项目:如何用HTML按钮启停while循环?

问题:ESP32 WebSocket控制循环启停失效

我正在使用ESP32(Arduino IDE)开发项目,尝试通过两个HTML按钮启停while循环。点击Start按钮发送start_transmission命令,点击Stop按钮发送stop_transmission命令,但收到停止命令时毫无反应。

前端代码(HTML/JS)

<p>
  <button type='button' id='BTN_START'>
    Start
  </button>
</p>

<p>
    <button type='button' id='BTN_STOP'>
      Stop
    </button>
</p>

</body>

<script>
  var Socket;
  var isTransmitting = false;

  document.getElementById('BTN_START').addEventListener('click', StartTrans);
  document.getElementById('BTN_STOP').addEventListener('click', StopTrans);
  
  function init() {
    Socket = new WebSocket('ws://' + window.location.hostname + ':81/');
    Socket.onmessage = function(event) {
      processCommand(event);
    };
  }
  

  function StartTrans() {
    var msg = {command:'start_transmission'};
    Socket.send(JSON.stringify(msg));
  }

  function StopTrans() {
    var msg = {command:'stop_transmission'};
    Socket.send(JSON.stringify(msg));
  }
</script>

Arduino端WebSocket事件处理代码

void webSocketEvent(byte num, WStype_t type, uint8_t * payload, size_t length) {
  switch (type) {
    case WStype_DISCONNECTED:
      Serial.println("Client " + String(num) + " disconnected");
      break;
    case WStype_CONNECTED:
      Serial.println("Client " + String(num) + " connected");
      break;
    case WStype_TEXT:
      String command = "";
      StaticJsonDocument<200> doc;
      DeserializationError error = deserializeJson(doc, payload, length);
      if (!error) {
        command = doc["command"].as<String>();
        Serial.println("Received command: " + command);
      }
      
         do
        {   
            int data = ShortRead();
            Serial.print("Force is: ");
            Serial.println(data);
            delay(1000);  
            if(command == "stop_transmission"){break;}    
         } while (command == "start_transmission")
          
        
        Serial.println("");
      break;
  }
}

问题根源与解决方案

核心问题

  1. 事件处理函数被完全阻塞:当收到start_transmission命令后,代码进入do-while循环,里面的delay(1000)会阻塞ESP32的主线程。Arduino是单线程运行,事件处理函数不执行完毕,就无法处理新的WebSocket消息(包括Stop命令)。
  2. 命令变量只读取一次:循环内的command变量是事件触发时读取的固定值,后续即使收到Stop命令,循环里的command也不会更新,自然无法触发break。

修正方案

1. 用全局volatile标志位控制启停

定义全局的volatile bool isTransmitting = false;,通过标志位的状态来控制主循环中的数据读取逻辑,避免在事件处理函数内执行阻塞操作。

2. 采用非阻塞延时替代delay()

用millis()实现延时,让ESP32在等待期间能处理WebSocket消息、系统任务等,不会阻塞主线程。

3. 重构代码结构

将数据读取逻辑移到loop()函数中,确保webSocket.loop()能持续执行,处理新的WebSocket事件。

修改后的完整代码

Arduino端代码

#include <WebSocketsServer.h>
#include <ArduinoJson.h>

WebSocketsServer webSocket = WebSocketsServer(81);
volatile bool isTransmitting = false;
unsigned long previousMillis = 0;
const long interval = 1000; // 1秒读取间隔

void setup() {
  Serial.begin(115200);
  webSocket.begin();
  webSocket.onEvent(webSocketEvent);
}

void loop() {
  webSocket.loop(); // 必须持续调用,处理WebSocket事件
  
  if (isTransmitting) {
    unsigned long currentMillis = millis();
    // 非阻塞延时,到达间隔才执行读取
    if (currentMillis - previousMillis >= interval) {
      previousMillis = currentMillis;
      int data = ShortRead();
      Serial.print("Force is: ");
      Serial.println(data);
    }
  }
}

void webSocketEvent(byte num, WStype_t type, uint8_t * payload, size_t length) {
  switch (type) {
    case WStype_DISCONNECTED:
      Serial.println("Client " + String(num) + " disconnected");
      isTransmitting = false; // 断开连接自动停止传输
      break;
    case WStype_CONNECTED:
      Serial.println("Client " + String(num) + " connected");
      break;
    case WStype_TEXT:
      String command = "";
      StaticJsonDocument<200> doc;
      DeserializationError error = deserializeJson(doc, payload, length);
      if (!error) {
        command = doc["command"].as<String>();
        Serial.println("Received command: " + command);
        
        if (command == "start_transmission") {
          isTransmitting = true;
          previousMillis = millis(); // 初始化计时起点
        } else if (command == "stop_transmission") {
          isTransmitting = false;
        }
      }
      break;
  }
}

// 假设这是你的数据读取函数,根据实际逻辑修改
int ShortRead() {
  // 示例:返回随机模拟数据
  return random(0, 100);
}

前端代码(优化初始化时机)

<p>
  <button type='button' id='BTN_START'>
    Start
  </button>
</p>

<p>
    <button type='button' id='BTN_STOP'>
      Stop
    </button>
</p>

</body>

<script>
  var Socket;
  var isTransmitting = false;

  document.getElementById('BTN_START').addEventListener('click', StartTrans);
  document.getElementById('BTN_STOP').addEventListener('click', StopTrans);
  
  // 页面加载完成后自动初始化WebSocket
  window.onload = init;
  
  function init() {
    Socket = new WebSocket('ws://' + window.location.hostname + ':81/');
    Socket.onmessage = function(event) {
      // 如需处理ESP32返回的消息,在这里实现processCommand逻辑
      // processCommand(event);
    };
  }
  

  function StartTrans() {
    var msg = {command:'start_transmission'};
    Socket.send(JSON.stringify(msg));
  }

  function StopTrans() {
    var msg = {command:'stop_transmission'};
    Socket.send(JSON.stringify(msg));
  }
</script>

关键说明

  • volatile关键字确保isTransmitting变量在事件处理函数和主循环之间的可见性,避免编译器优化导致读取旧值。
  • webSocket.loop()必须在loop()中持续调用,否则无法处理新的WebSocket连接和消息。
  • 非阻塞延时millis()让ESP32在等待期间能响应其他事件,解决了原代码中delay()阻塞的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 05:15:00