Long Press that does not count as a short press SOLVED

Hello everyone,

Spent the last couple of hours to try and figure this one out, but I can't.

I am designing a speedometer for an electric motorcycle conversion.

I have a working code that that displays what I want and, at a press of a button, switches the display between the different information I want it to show - that part works great.

However, because this is an evolving project I now want to add a feature where the TRIP display gets reset to zero when you hold the button for a longer period - and I cannot figure out how to do it.

The long press code is based on this one shown in the video:

https://www.youtube.com/watch?v=TD7vjJy0w8U

Basically what happens now is the moment I press the button for a longer period, the counter already increases to a next number, my display changes and I would like to avoid that. Basically, I tried an if statement that if the counter is at a given number and the button is being pressed for a longer time, an action (zeroing the trip) should take place, but like I said, I can't even get to that stage because the counter goes up...

Anyway, here's my code, hoping someone can point me in the right direction.

// Below is the OLED definition code - this is responsible for setting up the screen only.

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 32 // OLED display height, in pixels

// Declaration for an SSD1306 display connected to I2C (SDA, SCL pins)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

// Below is the speedometer background code - this is responsible for calculating speed, distance

#include <EEPROM.h>

unsigned long period_pulse = 0;
unsigned long last_pulse = 0;
int speed_kmph = 0;
float distance = 0;
float old_distance = 0;
float old_distance2 = 0;
float rev_distance = 0;
unsigned long revolution_count = 0;
unsigned long rev_new, rev_old, check_pulse;
float eeprom_trip_dist_read;
float eeprom_total_dist_read;
float Trip_distance;
float Total_distance;
float Trip_distance_multiplied;
float Total_distance_multiplied;
char read_get_eeprom = 0;
char write_eeprom = 0;
unsigned long Millis = 0;
unsigned long eeprom_interval = 600000;
unsigned long eeprom_oldMillis = 0;

#define circum 2.19

// Below is the code responsible for measuring the battery percentage.
// #define batt_pin A0
// #define volt_multiplier 19 // from voltage divider 88V to 4.63148V
// #define batt_min 60 // min=60 volt
// #define batt_max 92 //max= 92 volt
// int batt_analog=0;
// float batt_voltage=0;
// int batt_percent=0;

// Below is the code responsible for button setup

const int buttonPin = 7;     // the number of the pushbutton pin

int buttonPushCounter = 0;   // counter for the number of button presses
boolean buttonState = LOW;         // current state of the button
boolean lastButtonState = LOW;     // previous state of the button

// Below is the code responsible for long button press setup

int buttonStatePrevious = LOW;                      // previousstate of the switch

unsigned long minButtonLongPressDuration = 3000;    // Time we wait before we see the press as a long press
unsigned long buttonLongPressMillis;                // Time in ms when we the button was pressed
bool buttonStateLongPress = false;                  // True if it is a long press

const int intervalButton = 50;                      // Time between two readings of the button state
unsigned long previousButtonMillis;                 // Timestamp of the latest reading

unsigned long buttonPressDuration;                  // Time the button is pressed in ms

unsigned long currentMillis;          // Variabele to store the number of milleseconds since the Arduino has started


void setup() {
  Serial.begin(115200);

  // initialize the pushbutton pin as an input:
  pinMode(buttonPin, INPUT);

  // Below is the setup code for the screen only along with the welcome message

  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { // Address 0x3D for 128x64
    Serial.println(F("SSD1306 allocation failed"));
    for (;;);
  }
  delay(2000);
  display.clearDisplay();

  display.setTextSize(2);
  display.setTextColor(WHITE);
  display.setCursor(0, 10);
  // Display static text
  display.println("eOsa wita");
  display.display();
  delay(2000);
  display.clearDisplay();
  display.display();

  // Below is the setup code for the speed etc calculations

  pinMode(2, INPUT);
  attachInterrupt(digitalPinToInterrupt(2), Hall_sensor, RISING);  // Enable interruption pin 2 when going from LOW to HIGH.

  eeprom_trip_dist_read = EEPROM.read(0);
  eeprom_total_dist_read = EEPROM.read(3);
}


// Function for reading the button state
void ButtonStateRead() {

  // If the difference in time between the previous reading is larger than intervalButton
  if (currentMillis - previousButtonMillis > intervalButton) {

    // Read the digital value of the button (LOW/HIGH)
    int buttonState = digitalRead(buttonPin);

    // If the button has been pushed AND
    // If the button wasn't pressed before AND
    // IF there was not already a measurement running to determine how long the button has been pressed
    if (buttonState == HIGH && buttonStatePrevious == LOW && !buttonStateLongPress) {
      buttonLongPressMillis = currentMillis;
      buttonStatePrevious = HIGH;
      Serial.println("Button pressed");
    }

    // Calculate how long the button has been pressed
    buttonPressDuration = currentMillis - buttonLongPressMillis;

    // If the button is pressed AND
    // If there is no measurement running to determine how long the button is pressed AND
    // If the time the button has been pressed is larger or equal to the time needed for a long press
    if (buttonState == HIGH && !buttonStateLongPress && buttonPressDuration >= minButtonLongPressDuration) {
      buttonStateLongPress = true;
      Serial.println("Button long pressed");
    }

    // If the button is released AND
    // If the button was pressed before
    if (buttonState == LOW && buttonStatePrevious == HIGH) {
      buttonStatePrevious = LOW;
      buttonStateLongPress = false;
      Serial.println("Button released");

      // If there is no measurement running to determine how long the button was pressed AND
      // If the time the button has been pressed is smaller than the minimal time needed for a long press
      // Note: The video shows:
      //       if (!buttonStateLongPress && buttonPressDuration < minButtonLongPressDuration) {
      //       since buttonStateLongPress is set to FALSE on line 75, !buttonStateLongPress is always TRUE
      //       and can be removed.
      if (buttonPressDuration < minButtonLongPressDuration) {
        Serial.println("Button pressed shortly");
      }
    }

    // store the current timestamp in previousButtonMillis
    previousButtonMillis = currentMillis;

  }

}

void loop() {

  currentMillis = millis();    // store the current time
  ButtonStateRead();           // read the button state

  // read the state of the pushbutton value:
  buttonState = digitalRead(buttonPin);

  // Define buffer for printing in dynamic text
  char buffer[10];

  // Step 1 - fetching already saved distances from EEPROM

  if (read_get_eeprom == 0)
  {
    Trip_distance_multiplied = eeprom_trip_dist_read;
    Total_distance_multiplied = eeprom_total_dist_read;
    read_get_eeprom = 1;
  }

  // Step 2 - calculate speed based on pulses

  speed_kmph = ceil((circum * 3600) / period_pulse); //km/h

  // Step 3 - calculate distance travelled

  old_distance = distance;
  distance = revolution_count * (circum / 10); //Km, but I change to m for now so it's easy to see if this is working
  rev_distance = distance - old_distance;
  Trip_distance_multiplied = Trip_distance_multiplied + rev_distance;
  Total_distance_multiplied = Total_distance_multiplied + rev_distance;
  Trip_distance = Trip_distance_multiplied / 100;
  Total_distance = Total_distance_multiplied / 100;

  // Step 4 - write new distance to EEPROM

  if ((distance - old_distance2) > 1)
  {
    EEPROM.write(0, Trip_distance_multiplied);
    EEPROM.write(3, Total_distance_multiplied);
    old_distance2 = distance;
    write_eeprom = 1;
  }

  // Step 5 - not sure what happens here, I don't really follow this part of the code

  rev_new = revolution_count;
  check_pulse = millis() - last_pulse;

  if (check_pulse > 3000)
  {
    if (rev_new == rev_old)
    {
      speed_kmph = 0;

      if (write_eeprom == 0)
      {
        EEPROM.write(0, Trip_distance_multiplied);
        EEPROM.write(3, Total_distance_multiplied);
        write_eeprom = 1;
      }

    }
  }
  rev_old = revolution_count;

  // This is the battery code, suppressed for now.

  //batt_analog = analogRead(batt_pin);
  //batt_voltage=((batt_analog*5)/1024)*volt_multiplier;
  //batt_percent = ((batt_voltage - batt_min) / (batt_max - batt_min)) * 100;

  // This is the display code setting, that switches over at a button press

  switch (buttonPushCounter) // choose what to display based on buttonPushCounter value
  {
    case 0:

      display.setCursor(0, 10);
      display.setTextColor(WHITE, BLACK);
      sprintf(buffer, "V %d km/h", speed_kmph);
      display.print(buffer);
      display.display();
      break;

    case 1:

      display.setCursor(0, 10);
      display.setTextColor(WHITE, BLACK);
      sprintf(buffer, "Trp %d.%02d", (int)Trip_distance, (int)(Trip_distance * 100) % 100);
      display.print(buffer);
      display.display();
      break;

    case 2:

      display.setCursor(0, 10);
      display.setTextColor(WHITE, BLACK);
      sprintf(buffer, "Ttl %d.%02d", (int)Total_distance, (int)(Total_distance * 100) % 100);
      display.print(buffer);
      display.display();
      break;

  }

  // compare the buttonState to its previous state

  if (buttonState != lastButtonState)
  {
    if (buttonState == HIGH)
    {
      // if the current state is HIGH then the button
      // went from off to on:
      buttonPushCounter++;  // add one to counter
      display.clearDisplay();
      display.display();
      if (buttonPushCounter > 2)
      {
        buttonPushCounter = 0;
      }

    }
    // save the current state as the last state,
    //for next time through the loop
    lastButtonState = buttonState;
  }

  // This section is a code that zero's trip value if the button is held for more than 2 seconds


}

void Hall_sensor()
{
  period_pulse = millis() - last_pulse;
  last_pulse = millis();
  revolution_count++;

}

Disclaimer, have not looked at your code.

When you close the switch, you record the time.

When you open the switch, you calculate the time the switch was closed.

If the switch was closed for (say < 1 second) you do one thing.

If the switch was closed for (say > 3 seconds) you do something else.

Eliminate the code that "already increases" the counter, and make the decision later.

Hmm, I think I know what you guys are getting at - the problem seems to be not related to the order in which the decision is made, but how it's made - the counter increases when the button is being pushed (switch closed) - but if I understand correctly what LarryD is saying, I should redo the code so that instead of using if (buttonState == HIGH), I should differentiate and base the action on how long the switch was closed for. I will give that a bash and report back.

Do this AFTER you have decided between short and long press.

Sounds good.

  • capture timestamp when switch is pressed
  • if timer expires (> short period) report long press
  • if button released before timer expires, report short press

FYI

//                              c h e c k S w i t c h e s ( )
//********************************************^************************************************
//
void checkSwitches()
{
  byte currentState;

  //*********************************                               s t a r t S w i t c h
  currentState = digitalRead(startSwitch);

  //has this switch changed state ?
  if (lastStartSwitch != currentState)
  {
    //update to the new switch state
    lastStartSwitch = currentState;

    //******************
    //has the switch been pushed ?
    if (currentState == PUSHED)
    {
      //record the time the switch closed
      timePushed = millis();

    }

    //******************
    //switch has been released
    else
    {
      //calculate the time the switch was closed
      timePushed = millis() - timePushed;
      
      if(timePushed < 1000)
      {
        //do something
      }
      
      else if (timePushed > 3000)
      {
        //do something else
      }
      
    }

  } //END of     if (lastStartSwitch != currentState)


  //*********************************
  //future switches go here
  //*********************************

} //END of   checkSwitches()

Re: Toggle Library

That leaves 224 possible features remaining for 1 push button when using the pressCode() function (example).

pressCode()

Description
  • Up to 225 possible codes with one button. The returned code (byte) is easy to interpret when viewed in hex format. For example, 47 is 4 long, 7 short presses. F2 is double-click, F7 is 7 Fast clicks.
  • Fast-click mode is detected if the first 2 clicks (presses) are less than 0.4 sec, then counts any extra presses if the duration is less than 1 sec, up to 15 max (code FF)
  • Detection of long (greater than 1 sec) presses and short (less than 1 sec) presses occurs if the first press is 0.4 sec or longer.
  • Detect up to 15 short presses
  • Detect up to 14 long presses
  • Returns code after button is released for 1.4 sec
  • simplifies your code while adding maximum functionality to one button

Give this a trial run on WOKWi using an UNO here.

1 Like

Start counting when button goes active but don't TAKE ACTION until button is released. Short time, short action. Long time, long action, easy-peasy.

why wait for the timeout if the switch is release before then?

This can now be closed - I used the if statements from the tutorial I mentioned above and just added my actions there - worked as a charm. Thanks a lot for the pointer on how to approach this.

1 Like

You can mark the thread Solved

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