Simple Arduino Problem (Long and Short Press using Millis)

Hello my friends,

we've got an arduino uno and need to implement a long and short press using milli with 2 buttons. They should work as follows:

1st Button:
short press: increment parameter by 1
long press: increment parameter by 10 every second automatically while being pressed

2nd Button:
short press: decrement parameter by 1
long press: decrement parameter by 10 every second automatically while being pressed

Our prof want us to suffer so we are not allowed to use any libraries.
Here is the code:

#include <avr/io.h>
#include <avr/interrupt.h>

// Praktikum P04: 
// Taster entprellen, Long Press 
// Version 2: Entprellen mit Timern - Polling in ISR


uint32_t lastDisplayTime = 0;
uint32_t cycleTimeDisplay = 300; 
// states : 
// 0 = nicht gedrĂĽckt
// 1 = gedrĂĽckt 
volatile int stateKeyUp = 0;
volatile int stateKeyDown = 0;

void setup() {
  TCCR1A = 0; 
  // Todo Setup Timer (Zählvariable bis 65535), Taktfrequenz Prozessor 16 MHz
  TIMSK1 |= (1 << TOIE1);
  TCCR1B = (1 << CS10) | (1 << CS11); 
  // 1 Sek TCCR1B = (1 << CS12); 
 
  Serial.begin(9600);
  sei(); 
}

void loop() {
  actTime = millis(); 
  if (actTime > lastDisplayTime + cycleTimeDisplay) {
    lastDisplayTime += cycleTimeDisplay;
    Serial.print("Parameter"); 
    Serial.println(parameter); 
  }
}

thanks for any help and or input for solving our problem.

Welcome to the forum

#include <avr/io.h>
#include <avr/interrupt.h>

Are you allowed to use these ?

Yes we are allowed, but these are the only 2

Try my secret template, once you understand the flow, you can use it in many cases. And it is just plain codes, no library or any dependency, good luck.

int LED1 = 12;
int LED2 = 13;
int button = 3;

boolean LED1State = false;
boolean LED2State = false;

long buttonTimer = 0;
long longPressTime = 250;

boolean buttonActive = false;
boolean longPressActive = false;

void setup() {

  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(button, INPUT);
}

void loop() {

  if (digitalRead(button) == HIGH) {

    if (buttonActive == false) {
      // do things once key pressed
      buttonActive = true;
      buttonTimer = millis();
    }

    if ((millis() - buttonTimer > longPressTime) && (longPressActive == false)) {
      // do things once long pressed
      longPressActive = true;
      LED1State = !LED1State;
      digitalWrite(LED1, LED1State);
    }

  } else {

    if (buttonActive == true) {

      if (longPressActive == true) {
        // do things after long pressed
        longPressActive = false;

      } else {
        //do things after key released
        LED2State = !LED2State;
        digitalWrite(LED2, LED2State);
      }

      buttonActive = false;
    }
  }
}

I always prefer to do this with a state machine:

  • Make the button an object (i.e. a class)
  • Have a variable in the button object that records its state
  • Differentiate between the states you foresee; e.g. "idle", "held", "short press", "long press"
  • Have the button object cycle through the states depending on the environment. E.g. as soon as the button is pressed, it transitions from "idle" to "held". If it's released within the short press period, transition to "short press". If it's held for longer than that, transition to "long press"
  • Make a function that can be called to retrieve the button's state. The return value denotes the state; you can simply return the variable that the object uses to maintain its state
  • Make the button object reset its state after a short or long press event is retrieved by a caller.

You can use millis() to keep track of time and an update() function in your button object that's called frequently from loop() to ensure the button object cycles through the states as desired.

The advantage of the above is that you don't have much redundancy in your code; the same code runs for both of your buttons, and it can easily be scaled up for many more buttons. You can cycle through button objects using arrays.

PS: I see you're configuring a timer. If you're not allowed to use millis(), you can use a timer that triggers an interrupt every millisecond to increment an unsigned long variable that you can use instead of millis(). If you can use millis(), you don't need to configure a timer at all.

Are you allowed to use these?

  • Serial
  • millis()
  • pinMode()
  • digitalRead()

A state machine is pretty much the way to go here.

For one button it could look like this:
button

Every box is a state. To change the state you need to walk an arrow.
Each arrow come with conditions that need to be met and when you walk an arrow, you need to do something.

e.g.

Arrow Idle -> Debounce
Conditions:
 * button == down
Actions:
 * Start timer


Arrow Debounce -> Idle
Conditions:
 * timer > 10ms
 * button == up
Actions:
 * <none>


Arrow Long -> Long
Conditions:
 * timer > 1s
Actions:
 * Start timer
 * var += 10
1 Like

@Rintin that's exactly what I meant. Nicely documented/explained.

Thanks to everyone who gave me some input. I was able to solve my problem and make my arduino program work.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.