Arduino主循环首次执行后引脚无法再次激活的技术求助
Arduino灌溉项目自动化修复方案
问题背景
我做了一个小型作物灌溉项目,预期功能如下:
- 通过RTC模块获取当前时间,若时间在6:00-17:00之间,按序触发各继电器控制电动阀门
- 当日为奇数日时,完成初始触发后暂停4小时,再次触发pin8对应的继电器
- 所有操作完成后,自动延迟到次日6:00再执行循环
但目前遇到的问题是:主循环首次执行后,引脚无法再次激活,必须手动按下Arduino板上的红色复位按钮才能重新触发逻辑,急需实现完全自动化的解决方案。
原问题代码
#include <Wire.h> #include "RTClib.h" RTC_DS1307 rtc; const int dayStart = 6; const int dayEnd = 17; const int pin8 = 8; const int pin7 = 7; const int pin6 = 6; const int pin5 = 5; const int pin4 = 4; const unsigned long pinActiveTime = 420000; unsigned long shutDown; void setup() { Wire.begin(); Serial.begin(9600); pinMode(pin8, OUTPUT); pinMode(pin7, OUTPUT); pinMode(pin6, OUTPUT); pinMode(pin5, OUTPUT); pinMode(pin4, OUTPUT); pinMode(LED_BUILTIN, OUTPUT); if (!rtc.begin()) { blinkLED(10); while (1); } } void loop() { //rtc.adjust(DateTime(__DATE__, __TIME__)); mainRoutine(); delay(60000); } void mainRoutine(){ DateTime now = rtc.now(); unsigned long delayTime; if (now.hour() >= dayStart && now.hour() < dayEnd) { activatePin(pin8); activatePin(pin7); activatePin(pin6); activatePin(pin5); activatePin(pin4); if (evenDAY(now.day()) == false){ delay(14400000); activatePin(pin8); } } delayTime = computeDelay(now.hour(),now.minute(),now.second()); delay(delayTime); } bool evenDAY(int day){ return day % 2 ==0; } void activatePin(int pin) { digitalWrite(pin, HIGH); delay(pinActiveTime); digitalWrite(pin, LOW); } unsigned long computeDelay(int currentHour, int currentMinutes, int currentSeconds) { if (currentHour >= dayStart ){ shutDown = ((24 - currentHour + dayStart)* 3600 - currentMinutes * 60 - currentSeconds)*1000; } else{ shutDown = ((dayStart - currentHour)* 3600 - currentMinutes * 60 - currentSeconds)*1000; } return shutDown; } void blinkLED(int times) { for (int i = 0; i < times; i++) { digitalWrite(LED_BUILTIN, HIGH); delay(500); digitalWrite(LED_BUILTIN, LOW); delay(500); } }
问题根源
- 长时间delay阻塞程序:
mainRoutine()中调用的delay(delayTime)是延迟到次日6点的时长(最长近24小时),加上loop里的delay(60000),直接导致程序陷入长时间休眠,无法正常循环检测时间。 - 奇数日4小时延迟阻塞:
delay(14400000)(4小时)同样会彻底卡住程序,期间无法更新时间或执行其他逻辑。 - 逻辑执行顺序错误:完成灌溉操作后直接延迟到次日,导致loop循环无法正常迭代,必须手动复位才能重新触发。
修复后的代码
#include <Wire.h> #include "RTClib.h" RTC_DS1307 rtc; const int dayStart = 6; const int dayEnd = 17; const int pins[] = {8,7,6,5,4}; // 按触发顺序排列引脚 const int pinCount = sizeof(pins)/sizeof(pins[0]); const unsigned long pinActiveTime = 420000; // 引脚激活时长(7分钟) const unsigned long oddDayWait = 14400000; // 奇数日二次触发等待时长(4小时) // 状态机枚举 enum IrrigationState { WAIT_FOR_START, RUN_IRRIGATION, WAIT_FOR_ODD_DAY_REPEAT, RUN_ODD_DAY_REPEAT, WAIT_FOR_NEXT_DAY }; IrrigationState currentState = WAIT_FOR_START; unsigned long stateStartTime = 0; int currentPinIndex = 0; bool hasRunToday = false; // 标记当日是否已执行灌溉 void setup() { Wire.begin(); Serial.begin(9600); // 初始化所有引脚为输出 for(int i=0; i<pinCount; i++){ pinMode(pins[i], OUTPUT); digitalWrite(pins[i], LOW); } pinMode(LED_BUILTIN, OUTPUT); if (!rtc.begin()) { blinkLED(10); while (1); // RTC初始化失败则死循环 } } void loop() { DateTime now = rtc.now(); switch(currentState){ case WAIT_FOR_START: // 检查是否到当日灌溉时间且未执行过 if(now.hour() >= dayStart && now.hour() < dayEnd && !hasRunToday){ currentState = RUN_IRRIGATION; currentPinIndex = 0; stateStartTime = millis(); Serial.println("开始灌溉流程"); digitalWrite(pins[currentPinIndex], HIGH); // 激活第一个引脚 } // 过了17点重置当日执行标记 else if(now.hour() >= dayEnd){ hasRunToday = false; } break; case RUN_IRRIGATION: // 当前引脚激活时长到后切换下一个 if(millis() - stateStartTime >= pinActiveTime){ digitalWrite(pins[currentPinIndex], LOW); currentPinIndex++; // 所有引脚执行完成 if(currentPinIndex >= pinCount){ hasRunToday = true; // 奇数日则进入等待二次触发状态 if(now.day() % 2 != 0){ currentState = WAIT_FOR_ODD_DAY_REPEAT; stateStartTime = millis(); Serial.println("等待4小时后二次触发pin8"); } // 偶数日直接进入次日等待 else{ currentState = WAIT_FOR_NEXT_DAY; Serial.println("灌溉完成,等待次日"); } } // 激活下一个引脚 else{ digitalWrite(pins[currentPinIndex], HIGH); stateStartTime = millis(); Serial.print("激活引脚: "); Serial.println(pins[currentPinIndex]); } } break; case WAIT_FOR_ODD_DAY_REPEAT: // 等待4小时后触发pin8 if(millis() - stateStartTime >= oddDayWait){ currentState = RUN_ODD_DAY_REPEAT; stateStartTime = millis(); digitalWrite(pins[0], HIGH); // pins[0]是pin8 Serial.println("二次触发pin8"); } break; case RUN_ODD_DAY_REPEAT: // pin8激活时长到后关闭,进入次日等待 if(millis() - stateStartTime >= pinActiveTime){ digitalWrite(pins[0], LOW); currentState = WAIT_FOR_NEXT_DAY; Serial.println("二次触发完成,等待次日"); } break; case WAIT_FOR_NEXT_DAY: // 次日6点重置状态,回到等待触发 if(now.hour() >= dayStart && now.hour() < dayEnd && !hasRunToday){ currentState = WAIT_FOR_START; } break; } delay(1000); // 每秒检查一次状态,降低CPU占用 } void blinkLED(int times) { for (int i = 0; i < times; i++) { digitalWrite(LED_BUILTIN, HIGH); delay(500); digitalWrite(LED_BUILTIN, LOW); delay(500); } }
关键改进点
- 非阻塞状态机:用状态机替代长时间
delay(),程序始终保持响应,能实时检测时间变化 - 当日执行标记:避免同一日内重复执行灌溉流程
- 引脚批量管理:用数组管理继电器引脚,简化代码维护
- 清晰的流程拆分:将灌溉流程拆分为等待、执行、二次触发等待等独立状态,逻辑更清晰
- 低CPU占用:loop中仅做状态检查,每秒更新一次,减少资源消耗
内容的提问来源于stack exchange,提问作者M V
相关产品推荐
相关产品推荐

