Надежный кухонный таймер на Arduino с LCD-дисплеем
Компоненты и материалы
| Компонент | Количество |
|---|---|
| Arduino UNO | 1 |
| Adafruit Standard LCD 16x2 (белый на синем) | 1 |
| Тактильные кнопки 12 мм (SparkFun) | 4 |
| Резисторы 10 кОм | 4 |
| Резисторы 220 Ом (в списке 221 Ом) | 2 |
| Однооборотный потенциометр 100 кОм | 1 |
| Пьезо-зуммер | 1 |
| Плата для прототипирования и перемычки | 1 |
О проекте
Собственный кухонный таймер — отличный проект для изучения работы с кнопками, дисплеем и управлением временем. Устройство имеет четыре режима работы:
- IDLE (Ожидание) — начальный режим, отображает установленное время.
- SETUP (Настройка) — вход долгим нажатием кнопки S4 (Reset). Кнопка S3 (Start/Stop) выбирает поле (часы/минуты/секунды), S1 (Up) и S2 (Down) меняют значение.
- RUNNING (Отсчет) — запуск/остановка кнопкой S3. Выход — S3 или S4 (в IDLE).
- RINGING (Звонок) — срабатывает при достижении нуля. Любая кнопка останавливает звук и возвращает в IDLE.
Принцип работы
Каждая кнопка подключена через подтягивающий резистор 10 кОм к GND. При нажатии контакт замыкается на +5V. Опрос кнопок осуществляется в loop() с детектированием фронтов (изменения состояния). Для кнопки Reset реализовано определение длинного нажатия (счетчик циклов).
Управление режимами — switch(currentMode). Блок «Time management» считает оставшееся время через now() (библиотека TimeLib) и переключает в RINGING. Блок «LCD management» обновляет дисплей согласно режиму.
Схема подключения
Используйте любую плату для прототипирования. Зуммер можно разместить под проводами (см. фото/видео). На схеме показаны подключения LCD (пины 12,11,5,4,3,2), кнопок (пины 6,7,8,9), зуммера (пин 10) и потенциометра контраста.
Код (Arduino Kitchen Timer v1.0)
Требуются библиотеки: LiquidCrystal.h (стандартная) и TimeLib.h (Paul Stoffregen, установить через Library Manager или ZIP).
/**************************************************
* Arduino Kitchen Timer v1.0 - 27.01.2016
* By Angelo Fiorillo (Rome, IT)
* Лицензия: GNU GPL v3+
**************************************************/
#include <LiquidCrystal.h>
#include <TimeLib.h> // Фикс ошибки «'now' was not declared»
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;
int upButtonState = LOW, downButtonState = LOW;
int resetButtonPrevState = LOW, startStopButtonPrevState = LOW;
int upButtonPrevState = LOW, downButtonPrevState = LOW;
bool resetButtonPressed = false, resetButtonLongPressed = false;
bool startStopButtonPressed = false, upButtonPressed = false, downButtonPressed = false;
// Режимы
const int MODE_IDLE = 0;
const int MODE_SETUP = 1;
const int MODE_RUNNING = 2;
const int MODE_RINGING = 3;
int currentMode = MODE_IDLE;
int dataSelection = 0; // 0=часы, 1=минуты, 2=секунды (в SETUP)
long resetButtonLongPressCounter = 0;
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() {
// Сброс флагов нажатий
startStopButtonPressed = false;
upButtonPressed = false;
downButtonPressed = false;
/* --- Кнопка Reset (S4) с длинным нажатием --- */
resetButtonPressed = false;
resetButtonLongPressed = false;
resetButtonState = digitalRead(resetButtonPin);
if (resetButtonState != resetButtonPrevState) {
resetButtonPressed = (resetButtonState == HIGH);
resetButtonPrevState = resetButtonState;
} else {
if (resetButtonState == HIGH) {
resetButtonLongPressCounter++;
if (resetButtonLongPressCounter == 100) { // порог длинного нажатия
resetButtonPressed = false;
resetButtonLongPressed = true;
resetButtonLongPressCounter = 0;
}
} else {
resetButtonLongPressCounter = 0;
resetButtonPressed = false;
resetButtonLongPressed = false;
}
}
/* --- Кнопка Start/Stop (S3) --- */
startStopButtonState = digitalRead(startStopButtonPin);
if (startStopButtonState != startStopButtonPrevState) {
startStopButtonPressed = (startStopButtonState == HIGH);
startStopButtonPrevState = startStopButtonState;
}
/* --- Кнопка Down (S2) --- */
downButtonState = digitalRead(downButtonPin);
if (downButtonState != downButtonPrevState) {
downButtonPressed = (downButtonState == HIGH);
downButtonPrevState = downButtonState;
}
/* --- Кнопка Up (S1) --- */
upButtonState = digitalRead(upButtonPin);
if (upButtonState != upButtonPrevState) {
upButtonPressed = (upButtonState == HIGH);
upButtonPrevState = upButtonState;
}
/* --- МАШИНА СОСТОЯНИЙ --- */
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 + 60L * setupMinutes + 3600L * setupHours;
currentHours = setupHours; currentMinutes = setupMinutes; currentSeconds = setupSeconds;
dataSelection = 0; currentMode = MODE_IDLE;
}
if (startStopButtonPressed) { // Выбор поля
dataSelection++; if (dataSelection == 3) dataSelection = 0;
}
if (downButtonPressed) { // Уменьшение
switch (dataSelection) {
case 0: setupHours--; if (setupHours == -1) setupHours = 99; break;
case 1: setupMinutes--; if (setupMinutes == -1) setupMinutes = 59; break;
case 2: setupSeconds--; if (setupSeconds == -1) setupSeconds = 59; break;
}
}
if (upButtonPressed) { // Увеличение
switch (dataSelection) {
case 0: setupHours++; if (setupHours == 100) setupHours = 0; break;
case 1: setupMinutes++; if (setupMinutes == 60) setupMinutes = 0; break;
case 2: setupSeconds++; if (setupSeconds == 60) setupSeconds = 0; break;
}
}
break;
case MODE_RUNNING:
if (startStopButtonPressed) currentMode = MODE_IDLE; // Стоп
if (resetButtonPressed) { Reset(); currentMode = MODE_IDLE; } // Сброс
break;
case MODE_RINGING:
if (resetButtonPressed || startStopButtonPressed || downButtonPressed || upButtonPressed)
currentMode = MODE_IDLE; // Любая кнопка гасит звонок
break;
}
/* --- РАСЧЕТ ВРЕМЕНИ --- */
switch (currentMode) {
case MODE_IDLE: case MODE_SETUP: break; // ничего не делаем
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;
}
/* --- УПРАВЛЕНИЕ 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: ");
switch (dataSelection) {
case 0: lcd.print("HRS "); break;
case 1: lcd.print("MINS"); break;
case 2: lcd.print("SECS"); break;
}
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);
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;
}
Советы по сборке
- Потенциометр 100 кОм регулирует контраст LCD (средний контакт -> VO, крайние -> 5V и GND).
- Кнопки S1–S4: один контакт на +5V, второй на пин Arduino и через 10 кОм на GND.
- Зуммер: плюс на пин 10 (PWM), минус на GND. Если активный — просто HIGH/LOW, если пассивный —
tone(). - Питание от USB или Power Bank.
Производственный процесс
- Умный датчик полива «Thirst Alert» на Arduino — DIY сигнализация засухи для растений
- Создание осциллографа на Arduino Nano с дисплеем Nokia 5110 (20 кГц): простое DIY-руководство
- Умные шторы: автоматическое управление светом на Arduino с солнечной энергией
- Итальянские словесные часы на Arduino Nano, NeoPixel, LCD и RTC
- Сборка надежного таймера обратного отсчета на Arduino с 7-сегментным дисплеем SparkFun
- Надежный счетчик кВт·ч на Sigfox с Arduino MKR Fox 1200
- Bluetooth-монитор температуры на Arduino Nano с NTC-термистором
- Бесконтактный жестовый замок: APDS-9960 + Arduino для безопасного доступа
- Гальванически разделённый аналоговый вход для Arduino на LM331 и оптопаре 4N25
- Точный тест времени реакции на Arduino UNO