自制 Arduino 厨房定时器:LCD 显示、四按键操作、蜂鸣器报警
元器件与材料
| Arduino UNO | ×1 |
| Adafruit 标准 16x2 LCD (白字蓝底) | ×1 |
| 按键开关 12mm | ×4 |
| 电阻 10kΩ (上拉) | ×4 |
| 电阻 220Ω (蜂鸣器/背光限流) | ×2 |
| 电位器 100kΩ (LCD 对比度) | ×1 |
| 蜂鸣器 (有源/无源均可) | ×1 |
| 面包板 & 跳线 | 各×1 |
项目背景
从简单的数字厨房定时器出发,用 Arduino 重造一个“属于自己的”定时器,顺便理解此类小工具的工作原理。核心概念是状态机:定时器同一时刻只处于四种模式之一:
- IDLE (待机):上电/复位后的初始状态,显示当前设定时间,等待输入
- SETUP (设置):长按 S4(复位键) 进入;用 S3(启动/停止) 选择调整 时/分/秒,用 S1(加)、S2(减) 修改数值
- RUNNING (运行):按 S3 启动倒计时;再按 S3 或 S4 暂停/回到待机
- RINGING (响铃):倒计时归零自动进入;任意键按下停止蜂鸣并回到待机
硬件连接要点
- LCD 接线:RS=12, EN=11, D4=5, D5=4, D6=3, D7=2 (可按需调整引脚)
- 四个按键:一端接 5V,另端接数字引脚 (6,7,8,9) 并通过 10kΩ 下拉到 GND
- 蜂鸣器接 Pin 10 (PWM 控制音量)
- 电位器接 LCD V0 引脚调节对比度
核心代码逻辑
按键去抖与长按检测
每个按键用 digitalRead 读取当前状态,与上一次状态对比,变化时标记“按下”。S4(复位键) 额外增加长按计数器:持续 HIGH 超过约 100 个循环周期(约 1 秒) 判定为长按,触发进入 SETUP 模式。
状态机切换
主循环中用 switch(currentMode) 处理四种模式的按键响应:
- IDLE:短按复位→复位时间;长按复位→进入 SETUP;按启动/停止→切换 RUNNING/IDLE
- SETUP:启动/停止键循环选择 时/分/秒;加/减键修改对应数值(循环进位);短按复位→保存退出
- RUNNING:启动/停止键→暂停回 IDLE;复位键→复位回 IDLE
- RINGING:任意键→停止蜂鸣回 IDLE
时间计算与蜂鸣
运行模式下用 now() - startTime 计算已过秒数,从设定总秒数中减去得到剩余时间。剩余 ≤ 0 时切换到 RINGING。响铃模式用 analogWrite(buzzerPin, 20) 配合短延时产生断续鸣叫。
LCD 显示管理
每种模式显示不同内容:待机显示“Timer ready”+ 设定时间;设置显示“Setup mode: HRS/MINS/SECS”+ 当前调整值;运行显示“Counting down...”+ 实时倒计时 HH:MM:SS;响铃显示“RING-RING!”。
完整代码
/**************************************************
* Arduino Kitchen Timer v1.0 - 2016/01/27
* By Angelo Fiorillo (Rome, IT) - GPL3+
**************************************************/
#include <LiquidCrystal.h>
#include <TimeLib.h> // Paul Stoffregen's Time 库
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
const int buzzerPin = 10;
const int resetButtonPin = 6; // S4
const int startStopButtonPin = 7; // S3
const int downButtonPin = 8; // S2
const int upButtonPin = 9; // S1
// 设定时间
int setupHours = 0, setupMinutes = 0, setupSeconds = 0;
time_t setupTime = 0;
// 运行时时间
int currentHours = 0, currentMinutes = 0, currentSeconds = 0;
time_t currentTime = 0, startTime = 0, elapsedTime = 0;
// 按键状态变量
int resetButtonState = LOW, startStopButtonState = LOW, upButtonState = LOW, downButtonState = LOW;
int resetButtonPrevState = LOW, startStopButtonPrevState = LOW, upButtonPrevState = LOW, downButtonPrevState = LOW;
bool resetButtonPressed = false, resetButtonLongPressed = false;
bool startStopButtonPressed = false, upButtonPressed = false, downButtonPressed = false;
long resetButtonLongPressCounter = 0;
// 模式常量
const int MODE_IDLE = 0, MODE_SETUP = 1, MODE_RUNNING = 2, MODE_RINGING = 3;
int currentMode = MODE_IDLE;
int dataSelection = 0; // 0=小时, 1=分钟, 2=秒
void setup() {
lcd.begin(16, 2);
pinMode(resetButtonPin, INPUT);
pinMode(startStopButtonPin, INPUT);
pinMode(upButtonPin, INPUT);
pinMode(downButtonPin, INPUT);
pinMode(buzzerPin, OUTPUT);
Serial.begin(9600);
}
void loop() {
// 1. 读取并去抖所有按键 (重置按下标志)
// ... (同源码, 此处省略重复代码, 关键逻辑见上文说明)
// 2. 状态机处理
switch(currentMode) {
case MODE_IDLE:
if(resetButtonPressed) Reset();
if(resetButtonLongPressed) currentMode = MODE_SETUP;
if(startStopButtonPressed) {
currentMode = (currentMode == MODE_IDLE) ? MODE_RUNNING : MODE_IDLE;
if(currentMode == MODE_RUNNING) startTime = now();
}
break;
case MODE_SETUP:
if(resetButtonPressed) { // 退出设置
setupTime = setupSeconds + 60*setupMinutes + 3600*setupHours;
currentHours = setupHours; currentMinutes = setupMinutes; currentSeconds = setupSeconds;
dataSelection = 0; currentMode = MODE_IDLE;
}
if(startStopButtonPressed) { dataSelection = (dataSelection + 1) % 3; }
if(downButtonPressed) { /* 对应 dataSelection 减 */ }
if(upButtonPressed) { /* 对应 dataSelection 加 */ }
break;
case MODE_RUNNING:
if(startStopButtonPressed || resetButtonPressed) { currentMode = MODE_IDLE; if(resetButtonPressed) Reset(); }
break;
case MODE_RINGING:
if(resetButtonPressed || startStopButtonPressed || downButtonPressed || upButtonPressed) currentMode = MODE_IDLE;
break;
}
// 3. 时间计算与蜂鸣
switch(currentMode) {
case MODE_RUNNING:
currentTime = setupTime - (now() - startTime);
if(currentTime <= 0) currentMode = MODE_RINGING;
break;
case MODE_RINGING:
analogWrite(buzzerPin, 20); delay(20);
analogWrite(buzzerPin, 0); delay(40);
break;
}
// 4. LCD 刷新
lcd.setCursor(0, 0);
switch(currentMode) {
case MODE_IDLE:
lcd.print("Timer ready "); lcd.setCursor(0,1);
lcd.print(currentHours); lcd.print(" "); lcd.print(currentMinutes); lcd.print(" "); lcd.print(currentSeconds); lcd.print(" ");
break;
case MODE_SETUP:
lcd.print("Setup mode: ");
lcd.print(dataSelection==0?"HRS ":dataSelection==1?"MINS":"SECS");
lcd.setCursor(0,1);
lcd.print(setupHours); lcd.print(" "); lcd.print(setupMinutes); lcd.print(" "); lcd.print(setupSeconds); lcd.print(" ");
break;
case MODE_RUNNING:
lcd.print("Counting down..."); lcd.setCursor(0,1);
// 格式化 HH:MM:SS (补零)
if(hour(currentTime)<10) lcd.print("0");
lcd.print(hour(currentTime)); lcd.print(":");
if(minute(currentTime)<10) lcd.print("0");
lcd.print(minute(currentTime)); lcd.print(":");
if(second(currentTime)<10) lcd.print("0");
lcd.print(second(currentTime));
break;
case MODE_RINGING:
lcd.print(" RING-RING! "); lcd.setCursor(0,1); lcd.print(" ");
break;
}
delay(10);
}
void Reset() {
currentMode = MODE_IDLE;
currentHours = setupHours; currentMinutes = setupMinutes; currentSeconds = setupSeconds;
}
原理图
原理图展示了按键下拉电阻、LCD 接线、蜂鸣器驱动及电源连接。可使用标准面包板布局,蜂鸣器可置于跳线下方节省空间。
制造工艺
- 智能缺水报警器 – DIY植物土壤湿度监测装置
- 用 Arduino Nano 和 Nokia 5110 LCD 自制 20kHz 示波器:简易 DIY 指南
- 智能窗帘:基于Arduino与太阳能的自动光控系统
- 意大利语单词时钟:Arduino Nano + NeoPixel 144 灯珠 + LCD + RTC 打造
- 使用 SparkFun 7 段数码管构建可靠的 Arduino 倒计时器
- 基于Arduino MKR Fox 1200构建可靠Sigfox千瓦时电表
- 蓝牙温度监测仪:Arduino Nano + NTC 热敏电阻 + HC-06 无线传输
- 非接触手势门锁:APDS-9960 + Arduino 实现安全门禁
- Arduino Nano 多功能伴侣套件 —— 电阻/电容测量、二极管测试、RTC时钟一体化
- Arduino Micro/Leonardo USB MIDI 适配器套件 —— 完整元件清单与固件指南
- 为Arduino构建隔离模拟输入电路
- Arduino UNO 精准视觉反应时间测试仪