基于Arduino的自动冲水装置开发求助:LED逻辑控制卡壳
解决Arduino自动冲水装置的人员停留阶段逻辑问题
嘿,我来帮你搞定这个人员停留阶段的逻辑瓶颈!核心问题其实是要用状态机清晰区分「检测到人员进入」「人员停留」「人员离开」三个阶段,同时避免用delay()阻塞程序(不然你没法持续检测人员是否还在)。下面是完整的思路和代码实现:
先理清楚核心状态逻辑
我们把整个流程拆成4个状态,每个状态只做对应的事:
- IDLE(初始状态):所有LED熄灭,伺服电机复位,等待检测人员
- DETECTED(刚检测到人员):点亮第一个LED,1秒后自动熄灭,切换到停留状态
- STAY(人员停留):持续检测人员是否在范围内,只要在就保持第二个LED点亮;一旦人员离开,切换到离开状态
- LEAVE(人员离开):熄灭第二个LED,点亮第三个LED后随即熄灭,触发伺服电机冲水动作,最后回到初始状态
完整代码实现
#include <Servo.h> // 硬件引脚定义 const int trigPin = 12; const int echoPin = 11; const int led1Pin = 10; // 第一个LED:检测到人员时亮1秒 const int led2Pin = 9; // 第二个LED:人员停留期间常亮 const int led3Pin = 8; // 第三个LED:人员离开后闪一下 const int servoPin = 7; // 伺服电机引脚 // 状态定义 enum DeviceState { IDLE, DETECTED, STAY, LEAVE }; DeviceState currentState = IDLE; // 时间变量(用于非阻塞延时) unsigned long led1Timer = 0; unsigned long led3Timer = 0; const unsigned long LED1_DURATION = 1000; // 第一个LED亮1秒 const unsigned long LED3_DURATION = 500; // 第三个LED亮500ms // 超声波检测参数 const int DISTANCE_THRESHOLD = 30; // 检测阈值:30cm内判定有人(可根据场景调整) float distance = 0.0; Servo flushServo; void setup() { // 初始化引脚模式 pinMode(trigPin, OUTPUT); pinMode(echoPin, INPUT); pinMode(led1Pin, OUTPUT); pinMode(led2Pin, OUTPUT); pinMode(led3Pin, OUTPUT); // 初始化伺服电机 flushServo.attach(servoPin); flushServo.write(0); // 复位到初始位置 // 串口调试(可选,方便看状态和距离) Serial.begin(9600); } void loop() { // 第一步:持续读取超声波距离 distance = readUltrasonicDistance(); // 第二步:根据当前状态执行对应逻辑 switch(currentState) { case IDLE: // 初始状态:所有LED熄灭,等待检测人员 digitalWrite(led1Pin, LOW); digitalWrite(led2Pin, LOW); digitalWrite(led3Pin, LOW); if(distance < DISTANCE_THRESHOLD) { // 检测到人员,切换到DETECTED状态 digitalWrite(led1Pin, HIGH); led1Timer = millis(); currentState = DETECTED; Serial.println("状态切换:IDLE -> DETECTED"); } break; case DETECTED: // 刚检测到人员:等待1秒后熄灭第一个LED,进入停留状态 if(millis() - led1Timer >= LED1_DURATION) { digitalWrite(led1Pin, LOW); digitalWrite(led2Pin, HIGH); currentState = STAY; Serial.println("状态切换:DETECTED -> STAY"); } break; case STAY: // 人员停留状态:持续检测,只要人还在就保持LED2亮 if(distance < DISTANCE_THRESHOLD) { // 人员仍在,保持LED2点亮 digitalWrite(led2Pin, HIGH); } else { // 人员离开,切换到LEAVE状态 digitalWrite(led2Pin, LOW); digitalWrite(led3Pin, HIGH); led3Timer = millis(); currentState = LEAVE; Serial.println("状态切换:STAY -> LEAVE"); } break; case LEAVE: // 人员离开状态:点亮LED3后熄灭,触发冲水 if(millis() - led3Timer >= LED3_DURATION) { digitalWrite(led3Pin, LOW); // 触发伺服电机冲水动作(示例:转到90度停留1秒后复位) flushServo.write(90); delay(1000); // 这里用delay没问题,因为已经到最后阶段了 flushServo.write(0); // 回到初始状态 currentState = IDLE; Serial.println("状态切换:LEAVE -> IDLE"); } break; } // 串口打印调试信息(可选) Serial.print("当前距离:"); Serial.print(distance); Serial.print("cm | 当前状态:"); Serial.println(currentState); delay(100); // 降低串口打印频率,不影响主逻辑 } // 封装超声波读取函数 float readUltrasonicDistance() { digitalWrite(trigPin, LOW); delayMicroseconds(2); digitalWrite(trigPin, HIGH); delayMicroseconds(10); digitalWrite(trigPin, LOW); long duration = pulseIn(echoPin, HIGH); // 计算距离:声速340m/s = 0.034cm/µs,往返距离所以除以2 float cm = duration * 0.034 / 2; return cm; }
关键细节说明
- 状态机模式:用
enum定义状态,switch-case处理每个状态的逻辑,避免了混乱的嵌套判断,让代码更清晰 - 非阻塞延时:用
millis()代替delay(),这样在等待LED1熄灭的过程中,程序依然能持续检测超声波距离(如果用delay(),这1秒内没法检测人员是否离开) - 超声波距离读取:封装成函数,便于维护和修改
- 调试辅助:加入串口打印,方便你查看当前距离和状态,快速排查问题
注意事项
- 调整
DISTANCE_THRESHOLD的值,根据你的安装位置和使用场景,避免误触发或漏检测 - 伺服电机如果扭矩较大,建议用外部电源供电,不要直接用Arduino的5V引脚,防止供电不足
- 可以在
STAY状态里加入防抖逻辑(比如连续3次检测到距离超过阈值才判定离开),避免因为人员轻微移动导致误判
内容的提问来源于stack exchange,提问作者manan gandhi
相关产品推荐
相关产品推荐

