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; } }
问题根源与解决方案
核心问题
- 事件处理函数被完全阻塞:当收到
start_transmission命令后,代码进入do-while循环,里面的delay(1000)会阻塞ESP32的主线程。Arduino是单线程运行,事件处理函数不执行完毕,就无法处理新的WebSocket消息(包括Stop命令)。 - 命令变量只读取一次:循环内的
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
相关产品推荐
相关产品推荐

