Arduino: Измерение времени нажатия кнопки

Это руководство описывает, как измерить длительность нажатия кнопки на Arduino. Представлены два примера скетчей, которые выполняют одну и ту же задачу, но с разной организацией кода.

Основная идея: когда кнопка нажата (контакт замкнут), запускается таймер и в Serial Monitor отображается «Pressed» («Нажато»). При отпускании (контакт разомкнут) таймер останавливается, и в Serial Monitor выводится общее время нажатия.

Схема

Схема очень простая: Arduino с кнопкой, подключённой к пину D12, с подтягивающим резистором 10 кОм к земле.

Макетная плата с кнопкой

Схема подключения кнопки к Arduino

Пример 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

Serial Monitor -- пример вывода 1

Serial Monitor -- пример вывода 2

Serial Monitor -- пример вывода 3

Serial Monitor -- пример вывода 4

Пример 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)

Serial Monitor -- вывод примера 2

Ключевые улучшения

  • Код разделён на отдельные функции для ясности

  • Основной цикл становится компактным и легко читаемым

  • Именованные константы заменяют магические числа для лучшего понимания кода

  • Упрощённая структура основного цикла с выделенными вспомогательными функциями

Второй подход демонстрирует лучшие практики организации кода, делая более крупные скетчи более управляемыми и поддерживаемыми.