Arduino Uno自动浇水系统:如何仅在传感器输入变化时启用舵机
Arduino自动浇水系统修复方案
问题根源
- 模拟传感器的微小噪声会导致读数频繁变化,触发重复浇水动作
- 缺少状态标记,无法在一次浇水后禁用舵机直至传感器状态发生有效变化
- 循环末尾冗余的
servo.write(0)可能干扰舵机正常工作 delay()函数阻塞程序,导致LCD更新、传感器读取暂停
修复后的代码
#include <LiquidCrystal_I2C.h> #include <Servo.h> #define SOIL_SENSOR A0 #define ULTRASONIC_TRIG 13 #define ULTRASONIC_ECHO A1 #define POTENTIOMETER A2 #define SERVO_PIN 3 #define PIEZO_PIN 9 #define SERVO_DURATION 5000 #define DEBOUNCE_THRESHOLD 10 // 去抖动阈值,过滤微小噪声 #define ULTRASONIC_THRESHOLD 80 #define SOIL_THRESHOLD 500 #define POTENTIOMETER_THRESHOLD 500 int soilValue = 0; int ultrasonicValue = 0; int potentiometerValue = 0; bool isWateringDisabled = false; // 标记是否禁用浇水 Servo servo; LiquidCrystal_I2C lcd(0x27, 16, 2); void setup() { pinMode(PIEZO_PIN, OUTPUT); pinMode(ULTRASONIC_TRIG, OUTPUT); pinMode(ULTRASONIC_ECHO, INPUT); pinMode(SOIL_SENSOR, INPUT); pinMode(POTENTIOMETER, INPUT); servo.attach(SERVO_PIN); lcd.init(); lcd.backlight(); lcd.setCursor(0,0); lcd.print("Soil:"); lcd.setCursor(0,1); lcd.print("Light:"); // 初始化传感器基准值 soilValue = analogRead(SOIL_SENSOR); ultrasonicValue = getUltrasonicDistance(); potentiometerValue = analogRead(POTENTIOMETER); } void loop() { int new_soilValue = analogRead(SOIL_SENSOR); int new_ultrasonicValue = getUltrasonicDistance(); int new_potentiometerValue = analogRead(POTENTIOMETER); // 更新LCD显示,清除多余数字 lcd.setCursor(5, 0); lcd.print(new_soilValue); lcd.print(" "); lcd.setCursor(6, 1); lcd.print(new_potentiometerValue); lcd.print(" "); // 检查传感器是否发生有效变化(过滤噪声) bool hasSensorChanged = abs(new_soilValue - soilValue) > DEBOUNCE_THRESHOLD || abs(new_potentiometerValue - potentiometerValue) > DEBOUNCE_THRESHOLD || abs(new_ultrasonicValue - ultrasonicValue) > DEBOUNCE_THRESHOLD; if (hasSensorChanged) { // 更新基准值 soilValue = new_soilValue; potentiometerValue = new_potentiometerValue; ultrasonicValue = new_ultrasonicValue; // 传感器变化后,重新允许判断是否浇水 isWateringDisabled = false; } // 检查水位过低,触发蜂鸣器(非阻塞方式) if (ultrasonicValue > ULTRASONIC_THRESHOLD) { static unsigned long lastBeepTime = 0; if (millis() - lastBeepTime >= 1000) { tone(PIEZO_PIN, 1000); delay(500); noTone(PIEZO_PIN); lastBeepTime = millis(); } } // 满足浇水条件且未被禁用时,执行浇水 if (!isWateringDisabled && soilValue < SOIL_THRESHOLD && potentiometerValue > POTENTIOMETER_THRESHOLD && ultrasonicValue < ULTRASONIC_THRESHOLD) { servo.write(180); delay(SERVO_DURATION); // 若需非阻塞运行,可替换为millis()实现 servo.write(0); // 浇水完成后禁用,直到传感器再次变化 isWateringDisabled = true; } } int getUltrasonicDistance() { digitalWrite(ULTRASONIC_TRIG, LOW); delayMicroseconds(2); digitalWrite(ULTRASONIC_TRIG, HIGH); delayMicroseconds(10); digitalWrite(ULTRASONIC_TRIG, LOW); int duration = pulseIn(ULTRASONIC_ECHO, HIGH); int distance = duration * 0.034 / 2; return distance; }
关键修改说明
增加状态标记
isWateringDisabled:- 浇水完成后设为
true,禁止再次触发浇水动作 - 仅当传感器发生有效变化时,重置为
false,允许重新判断浇水条件
- 浇水完成后设为
传感器去抖动处理:
- 增加
DEBOUNCE_THRESHOLD,只有当读数变化超过阈值时,才判定为有效变化 - 避免模拟传感器的微小噪声导致重复触发
- 增加
移除冗余代码:
- 删除循环末尾的
servo.write(0),避免干扰舵机正常动作逻辑
- 删除循环末尾的
优化蜂鸣器逻辑:
- 使用
millis()实现非阻塞蜂鸣,避免阻塞程序影响LCD更新和传感器读取
- 使用
初始化基准值:
- 在
setup()中读取初始传感器值,避免设备启动时误触发浇水或报警
- 在
内容的提问来源于stack exchange,提问作者Dede Kurniawan
相关产品推荐
相关产品推荐

