Matrix Keypad longpress

Hello all.
I want to implement a long-press on a 4*4 keypad using Arduino UNO.
This long press is for a particular key alone (key D).
I'm having trouble implementing that.

In the code, I want to able to run everything that the short press of key B does by long-pressing key D.
I have been able to implement a long key press but I want to limit this to only work for key D as said earlier.
I will take out the use of key B when I get it working

Here's my code so far.
It does nothing as is but when I remove the if statement involving key D, it responds to long presses from all keys.
I left out the setup and other parts because the code is quite long.

unsigned long t_hold; // I declared this before setup


void loop() {
  keypressed = myKeypad.getKey(); //Constantly waiting for a key to be pressed

  if (myKeypad.getState() == HOLD) {
    if(keypressed == 'D'){
      if ((millis() - t_hold) > 500) {
        lcd.clear();
        lcd.print("Change Master Pin?");
      }
    }
  }

  if (keypressed == 'B') {
    //do this
  }
}

Please post a minimal but complete sketch that illustrates the problem

Have you tried printing keypressed before testing its value ?
Is it what you expect

Incidentally this line of code

  keypressed = myKeypad.getKey(); //Constantly waiting for a key to be pressed

does not do what the comment says, rather the function returns a key value immediately whether or not a key is pressed. If no key is pressed it returns zero, which you can test for

By way of contrast, the waitForKey() function does what its name implies, ie what the comment says

do you want to trigger only upon release of the 'D' long press or you want to trigger an action as soon as 'D' has been held down long enough?

how should other key behave? being taken into account as soon as they are depressed or upon release?

Let me go ahead to post the full code but I thought I'd leave that out so the context of my issue would be gotten right.

For the waitForKey() function you mentioned, would it not mean that I can't use the getKey in the same loop at the same time?
I want to be able to short press key D for one action and long press same key for a different action.

But, here's the full code.

#include <LiquidCrystal.h>
#include <Keypad.h>
#include <EEPROM.h>


int buzzer = 13;
int relay = 12;

int stats = 1; // this is to monitor the status of the system so that the text message containing password can only act when the system is off
int xpx = 0;//this is used to ensure that the system will not lock if unmatching codes are set while setting codes.
int count;//this is to restrict the maximum number of trials of pin to three(3)
int xppl = 0;
//long timer = 0;//not yet implemented but this attempts to keep track of how long the system has been down

String inputString;
long inputInt;

// initialize the library by associating any needed LCD interface pin
// with the arduino pin number it is connected to
const int rs = A0, en = A1, d4 = A2, d5 = A3, d6 = 1, d7 = 0;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);


const byte numRows = 4;         //number of rows on the keypad
const byte numCols = 4;         //number of columns on the keypad

//The keymap for the keypad

char keymap[numRows][numCols] =
{
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

char keypressed;
char waitKey;
char code[4];//change to 5 to compensate for a null terminator if char array has, not too sure at this point
//char code[] = {'1', '2', '1', '2'}; //The default code, you can change it or make it a 'n' digits one

char master[4]; //= {'1', '1', '1', '1'}; //This is the master pin to open the system if the set pin is forgotten

char mastercompare[sizeof(master)];
char master_buff1[sizeof(master)];  //Where the new key is stored
char master_buff2[sizeof(master)];  //Where the new key is stored again so it's compared to the previous one

char code_buff1[sizeof(code)];  //Where the new key is stored
char code_buff2[sizeof(code)];  //Where the new key is stored again so it's compared to the previous one

short a = 0, i = 0, s = 0, j = 0, v = 0, q = 0; //Variables used later

byte rowPins[numRows] = {9, 8, 7, 6}; //Rows 0 to 3 //if you modify your pins you should modify this too
byte colPins[numCols] = {5, 4, 3, 2}; //Columns 0 to 3


Keypad myKeypad = Keypad(makeKeymap(keymap), rowPins, colPins, numRows, numCols);

char holdkey;
unsigned long t_hold;

void setup() {

  pinMode(13, OUTPUT);//set buzzer to output
  pinMode(12, OUTPUT);//set relay to output
  lcd.begin(20, 4);

  //EEPROM retrieval of the stored master pin
  EEPROM.get(30, master[0]);
  EEPROM.get(31, master[1]);
  EEPROM.get(32, master[2]);
  EEPROM.get(33, master[3]);


  lcd.home();
  lcd.print("System Startup...");      //What's written on the LCD you can change

  delay(1000);

}

void loop() {

  //keypressed = myKeypad.getKey();               //Constantly waiting for a key to be pressed
  waitKey = myKeypad.waitForKey();
    if (myKeypad.getState() == HOLD) {
      if(keypressed == 'D'){
      if ((millis() - t_hold) > 500) {
        lcd.clear();
        lcd.print("Change Master Pin?");
        while (1) {
        }
      }
    
  }

  if (keypressed == 'B') {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Change Master Pin?");
    delay(1500);

    lcd.clear();
    lcd.print("Enter Old Pin:");
    lcd.setCursor(0, 2);
    lcd.print("Press A to verify");
    changemaster();

  }
  }
}

void changemaster() {
  v = 0;
  q = 0;
  while (keypressed != 'A') {          //A to confirm and quits the loop
    keypressed = myKeypad.getKey();
    if (keypressed != NO_KEY && keypressed != 'A') {
      lcd.setCursor(q, 1);
      lcd.print(keypressed); //shows the old keys
      //buzzonce();
      mastercompare[v] = keypressed;   //Store caracters in the array 'mastercompare'. we'll use this to cpmpare against the existing master before a change can be effected
      v++;
      q++;
    }
  }

  s = 0;
  for (i = 0; i < sizeof(master); i++) {
    if (mastercompare[i] == master[i])
      s++; //if s is then incremented up till 4, we know that each character was correct and it can proceed
  }

  if (s == sizeof(master)) {
    lcd.clear();
    lcd.print("Old Pin Confirmed");
    s = 0;
    delay(1200);
    keypressed = NO_KEY;

    GetNewMaster1();            //Get the new master pin
    GetNewMaster2();            //Get the new master pin again to confirm it
    s = 0;
    for (i = 0 ; i < sizeof(master) ; i++) { //Compare codes in array 1 and array 2 from two previous functions
      if (master_buff1[i] == master_buff2[i])
        s++;                                //again this how we verifiy, increment s whenever codes are matching
    }

    if (s == sizeof(master)) {        //Correct is always the size of the array
      j = 0;

      for (i = 0 ; i < sizeof(master) ; i++) {
        j = i + 30;
        master[i] = master_buff2[i];       //the master array now receives the new code
        EEPROM.put(j, master[i]);        //And stores it in the EEPROM

      }
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Master Pin Changed");
      lcd.setCursor(5, 1);
      lcd.print("successfully...");
      //buzzonce();
      xpx = 0;
      delay(2000);
    }
    else {                        //In case the new codes aren't matching
      lcd.clear();
      lcd.print("Numbers are not");
      lcd.setCursor(9, 1);
      lcd.print("matching!!");
      //buzzthrice();
      xpx = 1;
      delay(2000);
    }
  }
  else {
    lcd.clear();
    lcd.print("Not accepted");
    s = 0;
  }
}


void GetNewMaster1() {
  i = 0;
  j = 0;
  lcd.clear();
  lcd.print("Enter new Master pin");   //tell the user to enter the new code and press A
  lcd.setCursor(0, 1);
  lcd.print("Press A to confirm");
  delay(1800);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Enter new pin: ");
  lcd.setCursor(0, 2);
  lcd.print("Press A to confirm");     //Press A keep showing while the top row print ***

  while (keypressed != 'A') {          //A to confirm and quits the loop
    keypressed = myKeypad.getKey();
    if (keypressed != NO_KEY && keypressed != 'A' ) {
      lcd.setCursor(j, 1);
      lcd.print("*");               //On the new code you can show * as I did or change it to keypressed to show the keys
      //buzzonce();
      master_buff1[i] = keypressed;   //Store caracters in the array
      i++;
      j++;
    }
  }
  keypressed = NO_KEY;
}

void GetNewMaster2() {                        //This is exactly like the GetNewCode1 function but this time the code is stored in another array
  i = 0;
  j = 0;

  lcd.clear();
  lcd.print("Confirm your pin");
  lcd.setCursor(7, 1);
  lcd.print("and press A");
  delay(2000);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Enter pin: ");
  lcd.setCursor(0, 2);
  lcd.print("Press A to confirm");

  while (keypressed != 'A') {
    keypressed = myKeypad.getKey();
    if (keypressed != NO_KEY && keypressed != 'A' ) {
      lcd.setCursor(j, 1);
      lcd.print("*");
      //buzzonce();
      master_buff2[i] = keypressed;
      i++;
      j++;
    }
  }
  keypressed = NO_KEY;
}



/*if (myKeypad.getState() == HOLD) {
  if(keypressed == 'D') {
    if ((millis() - t_hold) > 500) {
      lcd.clear();
      lcd.print("Change Master Pin?");
      while (1) {
      }
    }
  }
  }*/

I want to be able to short press key D for one action and long press same key for a different action

is that only for D?
Do you want keys to be recognised upon PRESS or upon RELEASE?

if it's upon release, then you'll have a consistent behavior, and you'll only need to check for how long D was pressed to decide what to do.

if it's upon press, then 'D' will be a special case. You can't send an event upon press because you won't know if it's going to be a short press or a long press. You'll know that only when the duration for long press has expired

For the waitForKey() function you mentioned, would it not mean that I can't use the getKey in the same loop at the same time?

Both functions have their uses

If you don't mind the code blocking, or in fact want to take advantage of it to avoid the need to test whether a keypress has been made, then waitForKey() does that. If, however, you want the code not to be blocked then getKey() is the preferred function

Personally I would use getKey() and implement my own test for keystrokes by testing for a value of 0 or NO_KEY, which is defined as zero by the library, or false, which is to all intents and purposes equivalent to zero

I am not able to reply to both answers because I am quite new here and the time limit still applies so I have combined both into one.

For now I need this action to be carried out for key D alone.
Thanks for the precise explanation, it has to be upon release or still during press but as far as a particular time preiod has elapsed. From what I have in the code above, using the millis function, it performs as intended but everything breaks as soon as I try to restrict the action to key D alone.
This is what I am trying to fix.
How would I go about this?

Try this:

unsigned long start = millis();
while (keypad.getstate() == HOLD); // Wait for key to be released
if (millis() - start >= long_press_threshold)
{
  serial.print("Long press detected");
}

This will block but other parts of your code already does that, so..

a non blocking way could be something like this:

#include <Keypad.h> // https://github.com/Chris--A/Keypad

const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns

//define the symbols on the buttons of the keypads
char keys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};
const char longD = 'Z'; // char returned if long press on 'D'

byte rowPins[ROWS] = { 7, 6, 5, 4 };
byte colPins[COLS] = {11, 10, 9, 8};
Keypad kpd = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

const unsigned int longPressDuration = 3000;

char readKeypad()
{
  static bool dIsPressed = false;
  char key = NO_KEY;

  if (kpd.getKeys()) { // true if some activity
    for (int i = 0; key == NO_KEY && i < LIST_MAX; i++) {
      if (kpd.key[i].stateChanged ) {
        switch (kpd.key[i].kstate) {
          case PRESSED:
            if (kpd.key[i].kchar == 'D') {
              dIsPressed = true;
            } else key = kpd.key[i].kchar;
            break;
          case HOLD:
            if (kpd.key[i].kchar == 'D') {
              key = longD;
              dIsPressed = false;
            }
            break;
          case RELEASED:
            if (dIsPressed && kpd.key[i].kchar == 'D') {
              key = 'D';
              dIsPressed = false;
            }
            break;
          default: break;
        } // end switch
      } // end state changed
    } // end for
  }
  return key;
}

void setup() {
  Serial.begin(115200);
  kpd.setHoldTime(longPressDuration);
}

void loop() {
  char key = readKeypad();
  if (key != NO_KEY) Serial.println(key);
}

there is a "smart" function, readKeypad() that tells you what got pressed

All the keys get triggered directly upon pressing them, except 'D' where we have to wait a bit:

  • if you release 'D' before 3 seconds (longPressDuration) then you get a 'D' upon release
  • if you press 'D' for longer than 3 seconds then you get a 'Z' (longD) after the 3s

Thank you for the guide.
However, I have implemented this but don't seem to get any results.
There is no response from any key at all.
Below is my code.

#include <LiquidCrystal.h>
#include <Keypad.h>
#include <EEPROM.h>


int buzzer = 13;
int relay = 12;

int stats = 1; // this is to monitor the status of the system so that the text message containing password can only act when the system is off
int xpx = 0;//this is used to ensure that the system will not lock if unmatching codes are set while setting codes.
int count;//this is to restrict the maximum number of trials of pin to three(3)
int xppl = 0;
//long timer = 0;//not yet implemented but this attempts to keep track of how long the system has been down

String inputString;
long inputInt;

// initialize the library by associating any needed LCD interface pin
// with the arduino pin number it is connected to
const int rs = A0, en = A1, d4 = A2, d5 = A3, d6 = 1, d7 = 0;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);


const byte numRows = 4;         //number of rows on the keypad
const byte numCols = 4;         //number of columns on the keypad

//The keymap for the keypad

char keymap[numRows][numCols] =
{
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

const char longD = 'Z'; // char returned if long press on 'D'


char keypressed;
char waitKey;
char code[4];//change to 5 to compensate for a null terminator if char array has, not too sure at this point
//char code[] = {'1', '2', '1', '2'}; //The default code, you can change it or make it a 'n' digits one

char master[4]; //= {'1', '1', '1', '1'}; //This is the master pin to open the system if the set pin is forgotten

char mastercompare[sizeof(master)];
char master_buff1[sizeof(master)];  //Where the new key is stored
char master_buff2[sizeof(master)];  //Where the new key is stored again so it's compared to the previous one

char code_buff1[sizeof(code)];  //Where the new key is stored
char code_buff2[sizeof(code)];  //Where the new key is stored again so it's compared to the previous one

short a = 0, i = 0, s = 0, j = 0, v = 0, q = 0; //Variables used later

byte rowPins[numRows] = {9, 8, 7, 6}; //Rows 0 to 3 //if you modify your pins you should modify this too
byte colPins[numCols] = {5, 4, 3, 2}; //Columns 0 to 3


Keypad myKeypad = Keypad(makeKeymap(keymap), rowPins, colPins, numRows, numCols);

const unsigned int longPressDuration = 3000;

char holdkey;
unsigned long t_hold;

char readKeypad()
{
  static bool dIsPressed = false;
  char key = NO_KEY;

  if (myKeypad.getKeys()) { // true if some activity
    for (int i = 0; key == NO_KEY && i < LIST_MAX; i++) {
      if (myKeypad.key[i].stateChanged ) {
        switch (myKeypad.key[i].kstate) {
          case PRESSED:
            if (myKeypad.key[i].kchar == 'D') {
              dIsPressed = true;
            } else key = myKeypad.key[i].kchar;
            break;
          case HOLD:
            if (myKeypad.key[i].kchar == 'D') {
              key = longD;
              dIsPressed = false;
            }
            break;
          case RELEASED:
            if (dIsPressed && myKeypad.key[i].kchar == 'D') {
              key = 'D';
              dIsPressed = false;
            }
            break;
          default: break;
        } // end switch
      } // end state changed
    } // end for
  }
  return key;
}

void setup() {

  myKeypad.setHoldTime(longPressDuration);

  pinMode(13, OUTPUT);//set buzzer to output
  pinMode(12, OUTPUT);//set relay to output
  lcd.begin(20, 4);

  //EEPROM retrieval of the stored master pin
  EEPROM.get(30, master[0]);
  EEPROM.get(31, master[1]);
  EEPROM.get(32, master[2]);
  EEPROM.get(33, master[3]);


  lcd.home();
  lcd.print("System Startup...");      //What's written on the LCD you can change

  delay(1000);

}

void loop() {
  char key = readKeypad();
  if (key == 'D') {
    lcd.clear();
    lcd.print("Change Master Pin?");
    delay(1500);

    lcd.clear();
    lcd.print("Enter Old Pin:");
    lcd.setCursor(0, 2);
    lcd.print("Press A to verify");
    changemaster();

  }


void changemaster() {
  v = 0;
  q = 0;
  while (keypressed != 'A') {          //A to confirm and quits the loop
    keypressed = myKeypad.getKey();
    if (keypressed != NO_KEY && keypressed != 'A') {
      lcd.setCursor(q, 1);
      lcd.print(keypressed); //shows the old keys
      //buzzonce();
      mastercompare[v] = keypressed;   //Store caracters in the array 'mastercompare'. we'll use this to cpmpare against the existing master before a change can be effected
      v++;
      q++;
    }
  }

  s = 0;
  for (i = 0; i < sizeof(master); i++) {
    if (mastercompare[i] == master[i])
      s++; //if s is then incremented up till 4, we know that each character was correct and it can proceed
  }

  if (s == sizeof(master)) {
    lcd.clear();
    lcd.print("Old Pin Confirmed");
    s = 0;
    delay(1200);
    keypressed = NO_KEY;

    GetNewMaster1();            //Get the new master pin
    GetNewMaster2();            //Get the new master pin again to confirm it
    s = 0;
    for (i = 0 ; i < sizeof(master) ; i++) { //Compare codes in array 1 and array 2 from two previous functions
      if (master_buff1[i] == master_buff2[i])
        s++;                                //again this how we verifiy, increment s whenever codes are matching
    }

    if (s == sizeof(master)) {        //Correct is always the size of the array
      j = 0;

      for (i = 0 ; i < sizeof(master) ; i++) {
        j = i + 30;
        master[i] = master_buff2[i];       //the master array now receives the new code
        EEPROM.put(j, master[i]);        //And stores it in the EEPROM

      }
      lcd.clear();
      lcd.setCursor(0, 0);
      lcd.print("Master Pin Changed");
      lcd.setCursor(5, 1);
      lcd.print("successfully...");
      //buzzonce();
      xpx = 0;
      delay(2000);
    }
    else {                        //In case the new codes aren't matching
      lcd.clear();
      lcd.print("Numbers are not");
      lcd.setCursor(9, 1);
      lcd.print("matching!!");
      //buzzthrice();
      xpx = 1;
      delay(2000);
    }
  }
  else {
    lcd.clear();
    lcd.print("Not accepted");
    s = 0;
  }
}


void GetNewMaster1() {
  i = 0;
  j = 0;
  lcd.clear();
  lcd.print("Enter new Master pin");   //tell the user to enter the new code and press A
  lcd.setCursor(0, 1);
  lcd.print("Press A to confirm");
  delay(1800);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Enter new pin: ");
  lcd.setCursor(0, 2);
  lcd.print("Press A to confirm");     //Press A keep showing while the top row print ***

  while (keypressed != 'A') {          //A to confirm and quits the loop
    keypressed = myKeypad.getKey();
    if (keypressed != NO_KEY && keypressed != 'A' ) {
      lcd.setCursor(j, 1);
      lcd.print("*");               //On the new code you can show * as I did or change it to keypressed to show the keys
      //buzzonce();
      master_buff1[i] = keypressed;   //Store caracters in the array
      i++;
      j++;
    }
  }
  keypressed = NO_KEY;
}

void GetNewMaster2() {                        //This is exactly like the GetNewCode1 function but this time the code is stored in another array
  i = 0;
  j = 0;

  lcd.clear();
  lcd.print("Confirm your pin");
  lcd.setCursor(7, 1);
  lcd.print("and press A");
  delay(2000);
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("Enter pin: ");
  lcd.setCursor(0, 2);
  lcd.print("Press A to confirm");

  while (keypressed != 'A') {
    keypressed = myKeypad.getKey();
    if (keypressed != NO_KEY && keypressed != 'A' ) {
      lcd.setCursor(j, 1);
      lcd.print("*");
      //buzzonce();
      master_buff2[i] = keypressed;
      i++;
      j++;
    }
  }
  keypressed = NO_KEY;
}

I have also attached the 'ino' document.

if you take my code, modified to suit your keypad connection, does it show anything in the Serial console at 115200 bauds when you press the keys?

#include <Keypad.h> // https://github.com/Chris--A/Keypad

const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns

//define the symbols on the buttons of the keypads
char keys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};
const char longD = 'Z'; // char returned if long press on 'D'

byte rowPins[ROWS] = {9, 8, 7, 6}; 
byte colPins[COLS] = {5, 4, 3, 2}; 

Keypad kpd = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

const unsigned int longPressDuration = 3000;

char readKeypad()
{
  static bool dIsPressed = false;
  char key = NO_KEY;

  if (kpd.getKeys()) { // true if some activity
    for (int i = 0; key == NO_KEY && i < LIST_MAX; i++) {
      if (kpd.key[i].stateChanged ) {
        switch (kpd.key[i].kstate) {
          case PRESSED:
            if (kpd.key[i].kchar == 'D') {
              dIsPressed = true;
            } else key = kpd.key[i].kchar;
            break;
          case HOLD:
            if (kpd.key[i].kchar == 'D') {
              key = longD;
              dIsPressed = false;
            }
            break;
          case RELEASED:
            if (dIsPressed && kpd.key[i].kchar == 'D') {
              key = 'D';
              dIsPressed = false;
            }
            break;
          default: break;
        } // end switch
      } // end state changed
    } // end for
  }
  return key;
}

void setup() {
  Serial.begin(115200);
  kpd.setHoldTime(longPressDuration);
}

void loop() {
  char key = readKeypad();
  if (key != NO_KEY) Serial.println(key);
}

do you have an up to date keypad library (GitHub - Chris--A/Keypad: A version of the keypad library found in Wiring. This is just a copy made compatible with the Arduino IDE library manager.)

PS: Your code does not even compile... (and with delays in the code, chances of having something that is reactive are low)

Using your code and the serial monitor with that baud rate I only get displays for the numeric, * and # keys (they show as pressed). The key D doesn't respond when pressed or long pressed.

After installing the library from the link (should be updated), it still behaves the same too.

Check your keypad wires, seems you missed may be a connexion if you don’t see any of A B C D

The Keypad library allows you to set the HOLD time in milliseconds:

setHoldTime(uint hold);

Set that to your long-press time and the HOLD state will mean that the key has been held that long.
WARNING: You will always get the PRESSED state first, even if you eventually get the HOLD state. If you don't want to do the D-press action on a D-longpress, you should NOT act on the PRESSED state immediately. Just keep track of the last state of the D key. When the key is RELEASED, do the D-press action if the previous state was PRESSED but not if the previous state was HOLD.

Thanks, everyone.
It seems there was a shaky connection and it now works as intended.

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