如何修改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; } } }
关键细节说明
- 非阻塞定时:用
millis()记录每个继电器的状态切换时间,代替阻塞式的delay(),确保按键和继电器逻辑并行执行 - 按键消抖:通过记录上次按键的时间和状态,过滤机械按键的抖动信号,避免误触发
- 调节模式自动退出:按下按键后进入调节界面,5秒无操作自动返回运行状态,兼顾调节和正常运行需求
- 时长限制:设置99秒的时长上限,适配7段屏的显示范围,避免数值溢出
内容的提问来源于stack exchange,提问作者Jafar Farganlooj
相关产品推荐
相关产品推荐

