Using keypad.h HOLD to fire off continuous data?

I tried searching but didn't find a specific answer.

Can someone lend some guidance on the most feasible way to initiate a continuous output while HOLDING a keypad button? I worked with the examples in the IDE last night and I see that when a button is pressed and held past the hold timer it will initiate a single instance of whatever is contained in the HOLD statement. I would like to initiate a continuous output while the button his held down similar to how your computer keyboard functions.

I'd need to see your code to be certain but in general you can test for the HOLD condition and then repeat the key each time through the loop or after some time interval.

char holdKey;
unsigned long t_hold;

void loop(){
  char key = kpd.getKey();
  
   if (key){
     holdKey = key;
     Serial.println(key);
   }
  
   if (kpd.getState() == HOLD) {
      if ((millis() - t_hold) > 100 ) {
          Serial.println(holdKey);
          t_hold = millis();
      }
   }
}

I meant to post the code but was at work and didn't have access to the pc it was on.

This is the keypad code I've been using so far. It works well enough but as you can see there is no press and hold function. I'm really only interested in the last four case statements ( arrow up and down and the volume up and down) having the ability to repeat at about a 100 ms rate. I hope it makes sense in this code snippet but with the volume I'm basically just incrementing or deincrementing the volume byte in the array and adding the checksum base. I would like the ability to press and hold and after about a half second it takes off and ramps the volume up and down while held.

// Keypad actions
  {
    char key = keypad.getKey(); 
    {
      if(key)  // Check for a valid key.
      {
        switch (key)
        {
        case '1':
          Serial.write(button1, 10);
          Serial.write(buttonRelease, 10);
          break;    

        case '2':
          Serial.write(button2, 10);
          Serial.write(buttonRelease, 10);
          break;

        case '3':

          Serial.write(button3, 10);
          Serial.write(buttonRelease, 10);
          break;


       //-----  various case statements removed to shorten code for forum display




        case 'arrowUP':
          Serial.write(arrowUp, 10); 
          Serial.write(txACK,sizeof(txACK));
          Serial.write(buttonRelease, 10);  
          break;

        case 'arrowDown':
          Serial.write(arrowDown, 10); 
           Serial.write(txACK,sizeof(txACK));
          Serial.write(buttonRelease, 10); 
          break;

        case 'volUp':
          volume[4] = volumeVar;
          volume[6] = volumeVar + 0x6A;
          volumeVar = volumeVar + 0x01;
          Serial.write(volume, 8);
          break; 

        case 'volD':
          volume[4] = volumeVar;
          volume[6] = volumeVar + 0x6A;
          volumeVar = volumeVar - 0x01;
          Serial.write(volume, 8);
          break;

        
          }

          break;

        }
      }
    }

My confusion is how would I discriminate between which button is pressed using
if (kpd.getState() == HOLD) {
if ((millis() - t_hold) > 100 ) {
Serial.println(holdKey);
t_hold = millis();

I can place a bit of code within the statement and it will repeat when any key is held but I would like the code corresponding to each key to repeat when each individual key is held. That has be stumped at this point.

My confusion is how would I discriminate between which button is pressed using
if (kpd.getState() == HOLD) {
if ((millis() - t_hold) > 100 ) {
Serial.println(holdKey);
t_hold = millis();

If you can call kpd.getState() to determine that a key is being help down, why can't you call kpd.getKey() to figure out which key is being held down?

That's what I'm trying to do but I am not doing something correctly. I'm getting confused as to how to use it in conjunction with the HOLD test.

I'm getting lost in the logic.

You should only call getKey() once within the loop. You can call it in other places but it's possible that you could end up with a new state or an un-pressed key by the second call to getKey().

Once you've called "aKey = kpd.getKey()" and assigned it to holdKey then you simply see if holdKey is one of the arrow(?) keys and then test for HOLD.

Or the other way around. If you only care about the arrow keys being held down (meaning you'll ignore any other keys being held) then once you are inside the HOLD event you can test holdKey to see which arrow key is being held.

That's what I've done in this sketch.

#include <Keypad.h>

const byte ROWS = 4; //four rows
const byte COLS = 3; //three columns
char keys[ROWS][COLS] = {
  {'<','^','>'},
  {'4','5','6'},
  {'7','8','9'},
  {'*','0','#'}
};
byte rowPins[ROWS] = {5, 4, 3, 2}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {8, 7, 6}; //connect to the column pinouts of the keypad

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

char holdKey;
unsigned long t_hold;

void setup(){
  Serial.begin(9600);
}
  
void loop(){
  char key = keypad.getKey();
  
   if (key){
     holdKey = key;
     Serial.println(key);
   }
  
   if (keypad.getState() == HOLD) {
      if ((millis() - t_hold) > 100 ) {
          switch (holdKey) {
              case '<':
                  Serial.println("Move Left");
                  break;
              case '^':
                  Serial.println("Move Up");
                  break;
              case '>':
                  Serial.println("Move Right");
          }
          t_hold = millis();
      }
   }
}
1 Like

I'm getting lost in the logic.

Hi mancow, if the keypad library is causing confusion, consider not using it! Here is a sketch that fires off continuous data to the serial monitor whenever a key is pressed. It does not use keypad.h or difficult logic. Try it out - maybe you can adapt it to your project.

The keypad map is easy to make. The top row is the leftmost pin connected to each other pin. If you get continuity on a pin combination, type the character there. If not, put a zero as a placeholder. The next row starts with the 2 pin on the left, and so on.

// 3x4 keypad plugged into Arduino using pins 2-8
// must add 2 to any variable that is a pin number!

int i, j;
char pad[][7] = {{ 0 ,'2', 0 ,'0', 0 ,'8','5'},    // keypad map
                 { 0 , 0 ,'1', 0 ,'3', 0 , 0 },
                 { 0 , 0 , 0 ,'*', 0 ,'7','4'},
                 { 0 , 0 , 0 , 0 ,'#', 0 , 0 },
                 { 0 , 0 , 0 , 0 , 0 ,'9','6'}};
  
void setup() {
  Serial.begin(9600);
  for(i=0; i<7; i++)    
    pinMode(i+2, OUTPUT);
}

void loop() {
  for(j=0; j<5; j++) {                     // for first 5 pins
    pinMode(j+2, OUTPUT);                  // set to output and
    digitalWrite(j+2, LOW);                // bring one pin LOW
    for(i=j+1; i<7; i++) {                 // check pins to right
      pinMode(i+2, INPUT_PULLUP);          // which are held HIGH
      delay(2);                            // allow time to detect
      if(digitalRead(i+2)==LOW) {          // if one is found LOW
        Serial.println(pad[j][i]);         // print key character
      }
    }
    digitalWrite(j+2, HIGH);               // bring pin back to HIGH
  }
}

Wow that's interesting.

Thanks for the info, and thanks to all others that replied too. I will try the various ideas and try to get this going.

It works great now. Thank you!

mstanley:
You should only call getKey() once within the loop. You can call it in other places but it's possible that you could end up with a new state or an un-pressed key by the second call to getKey().

Once you've called "aKey = kpd.getKey()" and assigned it to holdKey then you simply see if holdKey is one of the arrow(?) keys and then test for HOLD.

Or the other way around. If you only care about the arrow keys being held down (meaning you'll ignore any other keys being held) then once you are inside the HOLD event you can test holdKey to see which arrow key is being held.

That's what I've done in this sketch.

#include <Keypad.h>

const byte ROWS = 4; //four rows
const byte COLS = 3; //three columns
char keys[ROWS][COLS] = {
  {'<','^','>'},
  {'4','5','6'},
  {'7','8','9'},
  {'*','0','#'}
};
byte rowPins[ROWS] = {5, 4, 3, 2}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {8, 7, 6}; //connect to the column pinouts of the keypad

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

char holdKey;
unsigned long t_hold;

void setup(){
  Serial.begin(9600);
}
 
void loop(){
  char key = keypad.getKey();
 
   if (key){
     holdKey = key;
     Serial.println(key);
   }
 
   if (keypad.getState() == HOLD) {
      if ((millis() - t_hold) > 100 ) {
          switch (holdKey) {
              case '<':
                  Serial.println("Move Left");
                  break;
              case '^':
                  Serial.println("Move Up");
                  break;
              case '>':
                  Serial.println("Move Right");
          }
          t_hold = millis();
      }
   }
}

Anyone have any ideas on a flip flop type toggle function that could be used with the keyapad? I would like a button to initiate an action when pressed and then initiate a different one when pressed again, back and forth similarly to a digital I/O pin with Hi/Lo status. I tried working with some boolean flags but am not hitting on the right combination of what to use.

I would like a button to initiate an action when pressed and then initiate a different one when pressed again, back and forth similarly to a digital I/O pin with Hi/Lo status. I tried working with some boolean flags but am not hitting on the right combination of what to use.

The only thing I use buttons for are to keep shirts closed.

If you are posting here, complaining about coding issues, don't you suppose that posting your code would help? I do.

The booleans are the key, but, of course, they must be accurately initializes, set, and reset.

Yes, bad terminology.

I would like to serially print two different strings of data. One string would be sent upon a keypad "button" press, the other on the following subsequent press of the same button and vice versa.

A software version of a flip flop is what I'm thinking of. A hardware interface example would be pressing # to initiate a solenoid to hold open a door lock, whereas the next press of the same # would release the solenoid engaging a lock when the said door has been closed.

I'm working with the generic keypad code. I tried several combinations of boolean states but became frustrated and wiped the slate so I am back to the plain keypad code with a single action assigned to each case.

I will post a better question after working with it again later on.

Apologies for wasting everyone's time.

I will figure it out eventually.

I will figure it out eventually.

OK, but, we're here to help.

I know, and it's appreciated.

I have a bad habit of getting to where I think something will work then I get frustrated and I delete it and start over to head in a different direction so I am not wading around the mess I just created. Plus, I hesitate to post something horrid because I feel like the dummy in the class while everyone is thinking, "man what a moron". :~ But, I do realize you can't help if you can't see it so I will stop doing that.

PaulS:

I will figure it out eventually.

OK, but, we're here to help.

Plus, I hesitate to post something horrid because I feel like the dummy in the class while everyone is thinking, "man what a moron".

In my opinion, the moron is the one that doesn't ask questions. Asking questions illustrates that you are trying to learn. It indicates that the teacher (whoever that is) has failed to properly present the material. Any new material should be presented only when you have a firm grasp of what has already been presented.

On a forum, it is hard to know where you are in the learning curve, and what you have already learned (or, rather, what has been presented with the expectation that you will have learned it).

Most of us are coders, with some level of professional experience, and we know what the learning curve looks like to new programmers. We're happy to help you get over the hump that you see. It's not really a hump, as there is no downhill on the other side. But, it does level out, and become relatively easy.

mancow:
Yes, bad terminology.

I would like to serially print two different strings of data. One string would be sent upon a keypad "button" press, the other on the following subsequent press of the same button and vice versa.

More terminology. This technique is normally called a toggle. In the code I show how you can do that using a single boolean called solenoid. You'll see that I preset it to false before setup().

Their are several methods of doing this and some of them are so magical that you can barely figure out what they are supposed to be doing. I figured this was your first try at toggling so I tried to make it as understandable as I could.

#include <Keypad.h>

const byte ROWS = 4; //four rows
const byte COLS = 3; //three columns
char keys[ROWS][COLS] = {
  {'<','^','>'},
  {'4','5','6'},
  {'7','8','9'},
  {'*','0','#'}
};
byte rowPins[ROWS] = {5, 4, 3, 2}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {8, 7, 6}; //connect to the column pinouts of the keypad

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

char holdKey;
unsigned long t_hold;

boolean solenoid = false;     // Make sure solenoid is off.

void setup(){
  Serial.begin(9600);
}
  
void loop() {
  char key = keypad.getKey();
  
   if (key){
     holdKey = key;
     Serial.println(key);
   }
  
   if (keypad.getState() == HOLD) {
      if ((millis() - t_hold) > 100 ) {
          switch (holdKey) {
              case '<':
                  Serial.println("Move Left");
                  break;
              case '^':
                  Serial.println("Move Up");
                  break;
              case '>':
                  Serial.println("Move Right");
          }
          t_hold = millis();
      }
   }
   
    // If the # key is pressed toggle the solenoid on/off.
    if ( key == '#' ) {
        if (solenoid==true) {
            solenoid = false;
            Serial.println("Solenoid turned OFF.");
        }
        else {
            solenoid = true;
            Serial.println("Solenoid turned ON.");
        }
    }
}

If you want a button to do something different depending on the if its been pressed or not, simply use variables. We understand you are new to this, but there's some programming fundamentals I think you're missing that if you understood, you may get a better idea of how to follow some of this logic that we're slinging around.

Initialize a variable at the top of your code, for example "int key_x_pressed = 0"

Then when key x is pressed, as part of that action code, set the variable to 1, or 0, depending on the condition you want. You would use an "if" condition to check the variable and act upon the result, therefor executing different code depending on the state of that variable.

Maybe i'm crazy, but that felt really hard to explain, even though it is one of the most basic of basic things in programming.

Use byte instead
byte key_x_pressed = 0;
to use 1 byte of memory to hold that 0/1 vs 2 bytes with an int.
byte can hold from 0 to 255, int will hold -32768 to 32767, or 0 to 65535 for unsigned int,

Same for declaring pin assignments, int is often used when byte is all that is needed.