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

如何修改Arduino代码实现继电器通断时长的用户可调功能?

解决方案

核心解决方向

  • 用**非阻塞定时(millis())**替代delay(),让按键输入和继电器运行能同时进行,彻底解决原代码阻塞导致的按键响应滞后问题
  • 给三个按键分配明确功能:
    • 按键1:循环切换要调节的继电器(对应1-4号)
    • 按键2:增加当前选中继电器的通电时长(每次加1秒,上限99秒)
    • 按键3:增加当前选中继电器的断电时长(每次加1秒,上限99秒)
  • 显示屏动态切换显示内容:运行时显示当前继电器的倒计时,调节时显示选中的继电器编号和对应时长

完整修改代码

#include <TM1637Display.h>

#define CLK 2
#define DIO 3
TM1637Display display(CLK, DIO);

// 继电器配置
int relayPins[] = {4, 5, 6, 7};
const int relayCount = 4;
unsigned long onTimes[] = {2000, 4000, 5000, 3000};   // 通电时长(毫秒)
unsigned long offTimes[] = {1000, 2000, 1000, 3000};  // 断电时长(毫秒)

// 继电器状态跟踪
bool relayStates[] = {false, false, false, false};
unsigned long lastStateChange[] = {0, 0, 0, 0};

// 按键配置(根据你的硬件引脚修改)
#define KEY_SELECT 8   // 切换继电器
#define KEY_ADD_ON 9   // 增加通电时长
#define KEY_ADD_OFF 10 // 增加断电时长
const int debounceDelay = 50;  // 按键消抖延迟
unsigned long lastKeyPress[3] = {0, 0, 0};
bool lastKeyState[3] = {HIGH, HIGH, HIGH}; // 默认高电平(适配INPUT_PULLUP模式)

// 调节模式变量
int selectedRelay = 0;  // 当前选中的继电器索引(0-3对应1-4)
bool showAdjustMode = false; // 是否显示调节界面
unsigned long lastModeToggle = 0;

void setup() {
  // 初始化继电器
  for (int i = 0; i < relayCount; i++) {
    pinMode(relayPins[i], OUTPUT);
    digitalWrite(relayPins[i], LOW);
  }
  
  // 初始化按键(用上拉输入,按键一端接引脚,另一端接地)
  pinMode(KEY_SELECT, INPUT_PULLUP);
  pinMode(KEY_ADD_ON, INPUT_PULLUP);
  pinMode(KEY_ADD_OFF, INPUT_PULLUP);
  
  display.setBrightness(0x0f);
  display.showNumberDec(selectedRelay + 1, false, 2, 0); // 初始显示选中的继电器编号
}

void loop() {
  handleRelays();       // 处理继电器定时逻辑
  handleKeyInputs();    // 处理按键输入
  updateDisplay();      // 更新显示屏内容
}

// 非阻塞处理继电器定时
void handleRelays() {
  unsigned long currentMillis = millis();
  
  for (int i = 0; i < relayCount; i++) {
    unsigned long targetDuration = relayStates[i] ? onTimes[i] : offTimes[i];
    
    // 检查是否到了切换状态的时间
    if (currentMillis - lastStateChange[i] >= targetDuration) {
      relayStates[i] = !relayStates[i];
      digitalWrite(relayPins[i], relayStates[i] ? HIGH : LOW);
      lastStateChange[i] = currentMillis;
    }
  }
}

// 处理按键输入(带消抖)
void handleKeyInputs() {
  unsigned long currentMillis = millis();
  bool currentKeyState;

  // 处理选择按键(切换继电器)
  currentKeyState = digitalRead(KEY_SELECT);
  if (currentKeyState != lastKeyState[0] && currentMillis - lastKeyPress[0] > debounceDelay) {
    if (currentKeyState == LOW) { // 按键按下触发
      selectedRelay = (selectedRelay + 1) % relayCount;
      showAdjustMode = true;
      lastModeToggle = currentMillis;
    }
    lastKeyState[0] = currentKeyState;
    lastKeyPress[0] = currentMillis;
  }

  // 处理增加通电时长按键
  currentKeyState = digitalRead(KEY_ADD_ON);
  if (currentKeyState != lastKeyState[1] && currentMillis - lastKeyPress[1] > debounceDelay) {
    if (currentKeyState == LOW) {
      onTimes[selectedRelay] += 1000; // 每次加1秒
      if (onTimes[selectedRelay] > 99000) onTimes[selectedRelay] = 99000; // 上限99秒
      showAdjustMode = true;
      lastModeToggle = currentMillis;
    }
    lastKeyState[1] = currentKeyState;
    lastKeyPress[1] = currentMillis;
  }

  // 处理增加断电时长按键
  currentKeyState = digitalRead(KEY_ADD_OFF);
  if (currentKeyState != lastKeyState[2] && currentMillis - lastKeyPress[2] > debounceDelay) {
    if (currentKeyState == LOW) {
      offTimes[selectedRelay] += 1000; // 每次加1秒
      if (offTimes[selectedRelay] > 99000) offTimes[selectedRelay] = 99000; // 上限99秒
      showAdjustMode = true;
      lastModeToggle = currentMillis;
    }
    lastKeyState[2] = currentKeyState;
    lastKeyPress[2] = currentMillis;
  }

  // 自动退出调节模式(5秒无操作)
  if (showAdjustMode && currentMillis - lastModeToggle > 5000) {
    showAdjustMode = false;
  }
}

// 更新显示屏内容
void updateDisplay() {
  if (showAdjustMode) {
    // 调节模式:交替显示继电器编号+通电时长、继电器编号+断电时长
    static unsigned long lastDisplaySwitch = 0;
    static bool showOnTime = true;
    unsigned long currentMillis = millis();
    
    if (currentMillis - lastDisplaySwitch > 1500) {
      showOnTime = !showOnTime;
      lastDisplaySwitch = currentMillis;
    }
    
    if (showOnTime) {
      // 例:显示"120"表示1号继电器通电20秒
      display.showNumberDecEx((selectedRelay + 1) * 100 + (onTimes[selectedRelay]/1000), 0b00000000, false, 4, 0);
    } else {
      // 例:显示"110"表示1号继电器断电10秒
      display.showNumberDecEx((selectedRelay + 1) * 100 + (offTimes[selectedRelay]/1000), 0b00000000, false, 4, 0);
    }
  } else {
    // 运行模式:显示当前正在计时的继电器倒计时
    for (int i = 0; i < relayCount; i++) {
      unsigned long remaining;
      if (relayStates[i]) {
        remaining = onTimes[i] - (millis() - lastStateChange[i]);
      } else {
        remaining = offTimes[i] - (millis() - lastStateChange[i]);
      }
      display.showNumberDec(remaining / 1000, false, 2, 0);
      break;
    }
  }
}

关键细节说明

  1. 非阻塞定时:用millis()记录每个继电器的状态切换时间,代替阻塞式的delay(),确保按键和继电器逻辑并行执行
  2. 按键消抖:通过记录上次按键的时间和状态,过滤机械按键的抖动信号,避免误触发
  3. 调节模式自动退出:按下按键后进入调节界面,5秒无操作自动返回运行状态,兼顾调节和正常运行需求
  4. 时长限制:设置99秒的时长上限,适配7段屏的显示范围,避免数值溢出

内容的提问来源于stack exchange,提问作者Jafar Farganlooj

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 14:39:51