Arduino HC-06蓝牙控制代码快速点击按钮执行两次的解决方法
解决方案
问题根源
快速点击按钮时,蓝牙模块会连续发送A(按下)和0(松开)指令,但原代码的while循环会阻塞程序,无法及时响应0信号;同时串口缓冲区的残留字符也会导致动作重复触发。此外,delay()函数会完全阻塞程序,让串口处理滞后。
修改思路
- 改用状态机模式:用全局变量标记伺服运行状态,替代阻塞式的
while循环,确保程序能实时响应串口输入。 - 非阻塞延迟:用
millis()替代delay(),避免程序被卡住,保证串口指令能被及时处理。 - 清空串口缓冲区:每次处理串口前清空残留数据,避免旧指令干扰。
修改后的代码
#include <Servo.h> Servo servo1U; Servo servo1L; Servo servo2U; Servo servo2L; Servo servo3U; Servo servo3L; Servo servo4U; Servo servo4L; int neutral = 90; int front_back = 0; int back_forward = 180; int up = 90; int down = 170; // 状态标记:是否正在执行伺服动作 bool isRunning = false; // 非阻塞延迟用的时间戳 unsigned long lastActionTime = 0; // 动作阶段标记(0-3对应原代码的4个动作步骤) int actionPhase = 0; const long actionInterval = 500; // 每个动作步骤的间隔时间 void setup() { Serial.begin(9600); servo1U.attach(9); servo1L.attach(2); servo2U.attach(3); servo2L.attach(4); servo3U.attach(5); servo3L.attach(6); servo4U.attach(7); servo4L.attach(8); // 初始化伺服到默认位置 servo1U.write(neutral); servo2U.write(neutral); servo3U.write(back_forward); servo4U.write(180 - back_forward); servo1L.write(down); servo2L.write(180 - down); servo3L.write(down); servo4L.write(180 - down); } void loop() { // 处理串口输入:先清空缓冲区残留数据,只取最新的指令 while (Serial.available() > 0) { char cha = Serial.read(); if (cha == 'A') { isRunning = true; } else if (cha == '0') { isRunning = false; // 停止时重置动作阶段 actionPhase = 0; } } // 如果处于运行状态,执行伺服动作(非阻塞) if (isRunning) { unsigned long currentTime = millis(); if (currentTime - lastActionTime >= actionInterval) { lastActionTime = currentTime; switch(actionPhase) { case 0: servo1U.write(180 - front_back); servo2U.write(front_back); servo3U.write(neutral); servo4U.write(neutral); break; case 1: servo1L.write(up); servo2L.write(180 - up); servo3L.write(up); servo4L.write(180 - up); break; case 2: servo1U.write(neutral); servo2U.write(neutral); servo3U.write(back_forward); servo4U.write(180 - back_forward); break; case 3: servo1L.write(down); servo2L.write(180 - down); servo3L.write(down); servo4L.write(180 - down); break; } // 切换到下一个动作阶段,循环执行 actionPhase = (actionPhase + 1) % 4; } } }
关键修改说明
- 状态机替代while循环:
isRunning变量标记是否需要执行动作,程序不再被阻塞,能实时响应串口的0指令,快速点击时会立即停止。 - 非阻塞延迟:用
millis()记录上次动作时间,确保每个动作步骤间隔500ms,但不会阻塞串口处理。 - 清空串口缓冲区:用
while(Serial.available() > 0)循环读取所有残留字符,只保留最新的指令,避免旧的A导致重复触发。 - 动作阶段拆分:把原代码的4个连续动作拆分为阶段,每次循环执行一个阶段,保证动作逻辑和原代码一致,但更灵活可控。
内容的提问来源于stack exchange,提问作者Olaf
相关产品推荐
相关产品推荐

