You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);                      
  }
}

问题根源

  1. 长时间delay阻塞程序:mainRoutine()中调用的delay(delayTime)是延迟到次日6点的时长(最长近24小时),加上loop里的delay(60000),直接导致程序陷入长时间休眠,无法正常循环检测时间。
  2. 奇数日4小时延迟阻塞:delay(14400000)(4小时)同样会彻底卡住程序,期间无法更新时间或执行其他逻辑。
  3. 逻辑执行顺序错误:完成灌溉操作后直接延迟到次日,导致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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 14:35:57