Arduino: Измерение времени нажатия кнопки
Это руководство описывает, как измерить длительность нажатия кнопки на Arduino. Представлены два примера скетчей, которые выполняют одну и ту же задачу, но с разной организацией кода.
Основная идея: когда кнопка нажата (контакт замкнут), запускается таймер и в Serial Monitor отображается «Pressed» («Нажато»). При отпускании (контакт разомкнут) таймер останавливается, и в Serial Monitor выводится общее время нажатия.
Схема
Схема очень простая: Arduino с кнопкой, подключённой к пину D12, с подтягивающим резистором 10 кОм к земле.
Пример 1: Вся логика в loop()
Первый скетч выполняет все операции внутри основного цикла loop().
Скетч использует простой дебаунс путём тройного чтения пина с короткими задержками для обеспечения надёжных показаний. Когда состояние переключателя меняется с LOW на HIGH (OPEN -> CLOSED), запускается таймер. Переход с HIGH на LOW (CLOSED -> OPEN) останавливает таймер и выводит время в миллисекундах в Serial Monitor.
// Sketch: Arduino - push button switch - time pressed 001
// Show the time a button switch has been pressed
// www.martyncurrey.com
// Pins
// D12 to push button switch with 10K ohm pull down resistor
const int PIN_PUSH_BUTTON_SWITCH = 12;
// variables
// switch status. Current and previous
boolean oldSwitchState = LOW;
// extra variables used for debounce
boolean newSwitchState1 = LOW;
boolean newSwitchState2 = LOW;
boolean newSwitchState3 = LOW;
// timer to record how long the switch is closed
long timer_timeStart = 0;
long timer_timeStop = 0;
void setup()
{
Serial.begin(9600);
Serial.print("Sketch: "); Serial.println(__FILE__);
Serial.print("Uploaded: "); Serial.println(__DATE__);
Serial.println(" ");
pinMode(PIN_PUSH_BUTTON_SWITCH, INPUT);
}
void loop()
{
// simple debounce
newSwitchState1 = digitalRead(PIN_PUSH_BUTTON_SWITCH); delay(1);
newSwitchState2 = digitalRead(PIN_PUSH_BUTTON_SWITCH); delay(1);
newSwitchState3 = digitalRead(PIN_PUSH_BUTTON_SWITCH);
// if all are the same value then we have a reliable reading
if ( (newSwitchState1==newSwitchState2) && (newSwitchState1==newSwitchState3) )
{
// check to see if the switch state has changed.
// This can be OPEN to CLOSED or CLOSED to OPEN.
if (newSwitchState1 != oldSwitchState)
{
// OPEN to CLOSED
if ( oldSwitchState == LOW && newSwitchState1 == HIGH )
{
// the switch has just been closed / button has been pressed.
// start the timer
timer_timeStart = millis();
Serial.print("Key pressed. ");
}
// CLOSED to OPEN
if ( oldSwitchState == HIGH && newSwitchState1== LOW )
{
// switch is OPEN and was CLOSED last time.
// stop timer and print the time the button was pressed
timer_timeStop = millis();
Serial.print("Key was pressed for ");
Serial.print(timer_timeStop - timer_timeStart );
Serial.println(" ms");
}
oldSwitchState = newSwitchState1;
} // if (switchState != oldSwitchState)
} // if ( (newSwitchState1==newSwitchState2) && (newSwitchState1==newSwitchState3) )
} // loop
Вывод в Serial Monitor
Пример 2: Рефакторинг с использованием функций
Эта версия реорганизует код с использованием выделенных функций. Основной цикл становится компактным и легко читаемым. Именованные константы заменяют магические числа для лучшего понимания кода.
// Sketch: Arduino - push button switch - time pressed 002
// Show the time a button switch has been pressed with added functions
// www.martyncurrey.com
// Pins
// D12 to push button switch with 10K ohm pull down resistor
const int PIN_PUSH_BUTTON_SWITCH = 12;
// variables
boolean switchState = LOW;
boolean oldSwitchState = LOW;
long timer_timeStart = 0;
long timer_timeStop = 0;
byte state = 0;
// These are not really required but using names rather than values makes the code easier to read.
const byte CLOSEDtoOPEN = 1;
const byte OPENtoCLOSED = 2;
void setup()
{
Serial.begin(9600);
Serial.print("Sketch: "); Serial.println(__FILE__);
Serial.print("Uploaded: "); Serial.println(__DATE__);
Serial.println(" ");
pinMode(PIN_PUSH_BUTTON_SWITCH, INPUT);
}
void loop()
{
state = checkSwitch();
if (state == OPENtoCLOSED) { startTimer(); }
if (state == CLOSEDtoOPEN) { stopTimer(); }
} // loop
int checkSwitch()
{
byte returnVal = 0;
// simple debounce
boolean newSwitchState1 = digitalRead(PIN_PUSH_BUTTON_SWITCH); delay(1);
boolean newSwitchState2 = digitalRead(PIN_PUSH_BUTTON_SWITCH); delay(1);
boolean newSwitchState3 = digitalRead(PIN_PUSH_BUTTON_SWITCH);
if ( (newSwitchState1==newSwitchState2) && (newSwitchState1==newSwitchState3) )
{
if (newSwitchState1 != oldSwitchState)
{
// OPEN => CLOSED
if ( oldSwitchState == LOW && newSwitchState1 == HIGH )
{
returnVal = OPENtoCLOSED;
}
// CLOSED => OPEN
if ( oldSwitchState == HIGH && newSwitchState1== LOW )
{
returnVal = CLOSEDtoOPEN;
}
oldSwitchState = newSwitchState1;
}
} // if ( (newSwitchState1==newSwitchState2) && (newSwitchState1==newSwitchState3) )
return returnVal;
} // int checkSwitch()
void startTimer()
{
// start the timer
timer_timeStart = millis();
Serial.print("Key pressed. ");
}
void stopTimer()
{
// stop the timer and print the total time the button was pressed
timer_timeStop = millis();
Serial.print("Key was pressed for ");
Serial.print(timer_timeStop - timer_timeStart );
Serial.println(" ms");
}
Вывод в Serial Monitor (Пример 2)
Ключевые улучшения
Код разделён на отдельные функции для ясности
Основной цикл становится компактным и легко читаемым
Именованные константы заменяют магические числа для лучшего понимания кода
Упрощённая структура основного цикла с выделенными вспомогательными функциями
Второй подход демонстрирует лучшие практики организации кода, делая более крупные скетчи более управляемыми и поддерживаемыми.