Getting a 4-digit number from a keypad

Hello everyone,
First of all I want to give my apologies because of my english and my programming capabilities.

The problem I have is quite easy, but I can´t make work this code:

#include <Keypad.h>



int myNum[]={0,0,0,0};
int total = 0; 

const byte ROWS = 4; 
const byte COLS = 4; 
char keys[ROWS][COLS] = {
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
  {'*','0','#','D'}
};
byte rowPins[ROWS] = {49,48,47,46}; //connect to row pinouts 
byte colPins[COLS] = {45,44,43,42}; //connect to column pinouts

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

void setup(){
                                    
  Serial.begin(9600); 
}

void loop()
{
  
Serial.println("Enter four-digit num -");  // request for input
            
            
       
 for (int i = 0; i < 4;){
               if(keypad.getKey()!= NO_KEY){                
               int tmp = keypad.getKey();
                          int getKey(tmp);
                          myNum[i]= tmp;
                          i = i+1;
                 }  
              }
              
 Serial.print("Entered number is = ");
  
        Serial.print(myNum[0]);
        Serial.print(myNum[1]);
        Serial.print(myNum[2]);
        Serial.println(myNum[3]);
       
        total = (myNum[0]*1000)+(myNum[1]*100)+(myNum[2]*10)+(myNum[3]); 
        
        delay(500);
         
}

It is going to be implemented in a bigger one, but anyway it is supposed to get the four digits pressed in a 4x4 keypad and make a whole number of them, storing it as "total"... but after showing in the serial "Enter four-digit num -" doesn't matters the key I press the result is always "0000"...
At least is better than the last time I edited the code... no giving back any number.
¿Can you show my mistake?

Thanks a lot!!

After recognising a key press you must wait until no keys are pressed
before sensing a new one - your current code will simply loop 4 times in
a few microseconds on the first keypress.

After recognising a key press you must wait until no keys are pressed

This assumes that the code is waiting for a key to be pressed. It is not.

OP: On any given pass through loop(), you have to determine if a key is pressed. If not, do nothing. If it is, add it to the array, in the proper place.

Of course, this means that you need to keep track of how many keys have been pressed.

There are some other things you should do. Do NOT assume that the password is exactly 4 characters. Of course, this means that you need some way to indicate that the password has been fully entered. You also need a backspace mechanism, in case the user makes a mistake, and wants to undo the last keypress. Fortunately, you DO have to other keys that could mean enter and backspace.

Hello,
MarkT:
Not really, It waits till the fourth key to be pressed before showing the result.

for (int i = 0; i < 4;){
               if(keypad.getKey()!= NO_KEY){                
               int tmp = keypad.getKey();
                          int getKey(tmp);
                          myNum[i]= tmp;
                          i = i+1;

Waits till i=3 (0, 1, 2, 3) before continuing

PaulS:
Is not a password, I want the number as a real int number, that's why at the last lines I count for "total"...
Backspace and enter (confirmation) will be next step to be done, but first I want to get the number (don't want to run before knowing how to walk...).
And is supposed the program is inside "for (int i = 0; i < 4;)" till gets four keys pressed.

Thanks for the replies, but I think is not the problem.

Cheers!!

Your problem is the use of a for loop to gather the key presses. To understand what is happening, consider the result when getKey() returns NO_KEY. The for loop does nothing and you print out the four zeros you initialized your array with. Restructure your code to check for a key press each time thru the loop() and store key presses until you get four of them.

And is supposed the program is inside "for (int i = 0; i < 4;)" till gets four keys pressed.

I don't know what you mean by this, but getKey() is NOT a blocking function. It is NOT waiting for you to press a key.

Hello,
DavidOConnor:
I understand what you mean, but in the sentence: if(keypad.getKey()!= NO_KEY) , I have the "!=" that means different (not equal), so has to wait till the pressing shows a key pressed. I'm right?

PaulS:
I think I haven't explained well. I suppose that for the control block "for" it has to wait for the condition to go on:

for (what_I_have; till_mets_this;)
code to be executed till what I have mets this

so:

for (int i = 0; i < 4;){
               if(keypad.getKey()!= NO_KEY){                
               int tmp = keypad.getKey();
                          int getKey(tmp);
                          myNum[i]= tmp;
                          i = i+1;
}

Has to wait till 4 keys are pressed to continue the code. And is what it makes, but without storing the values of: myNum*.*
The code is working perfectly except for that, doesn't stores the values, but waits for the four keys pressed.
Did I explained myself better this time? :blush:
Cheers!!

but in the sentence: if(keypad.getKey()!= NO_KEY) , I have the "!=" that means different (not equal), so has to wait till the pressing shows a key pressed. I'm right?

No, it does not wait.

This waits:

while(key = keyPad.getKey() == NO_KEY)
{
    // Do nothing
}
// Use key

But, you do NOT want to wait. You want to deal with events when they happen. You do not want to wait for them to happen.

one of the things I like to do is, setup a timer so that it will only wait for the 4 buttons to be press a set amount of time before it resets and goes back to waiting for the first key. Also you have a line int getKey(tmp); do a Serial.print of tmp before and after that to see what it is.

 int buttonpressed = 0;
     while(buttonpressed < 1){
       timer = millis();
       buttonState[thisPin] = digitalRead(buttonPins[thisPin]);
       buttonpressed += buttonState[thisPin];           
       }
       if(timer - mil > 10000) {buttonpressed = 1;}     
     }
     if(timer - mil < 10000){

    }

Hello,
I have modified the code, but the result is the same:

#include <Keypad.h>

int i = 0;
int choice;
int myInt =0;
int myNum[]={0,0,0,0};
int total = 0; 

const byte ROWS = 4; 
const byte COLS = 4; 
char keys[ROWS][COLS] = {
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
  {'*','0','#','D'}
};
byte rowPins[ROWS] = {49,48,47,46}; //connect to row pinouts 
byte colPins[COLS] = {45,44,43,42}; //connect to column pinouts

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

void setup(){
                                    
  Serial.begin(9600); 
}

void loop()
{
  
Serial.println("Enter four-digit num -");  // request for input
            
for (int i = 0; i < 4;){
while(keypad.getKey() == NO_KEY)
{
    // Do nothing
}
if(keypad.getKey()!= NO_KEY);
{                
 char key = keypad.getKey();
 int myInt = key;
 Serial.println("You have pressed ");
 Serial.print(key);
 Serial.println("The corresponding number is ");
 Serial.print(myInt);
 myNum[i]= myInt;
 i = i+1;
}
}

 Serial.println();             
 Serial.print("Entered number is = ");
  
        Serial.print(myNum[0]);
        Serial.print(myNum[1]);
        Serial.print(myNum[2]);
        Serial.println(myNum[3]);
       
        total = (myNum[0]*1000)+(myNum[1]*100)+(myNum[2]*10)+(myNum[3]); 
        
        delay(500);
        
        Serial.println(total);
}

Thank you very much jasit for the idea of printing through the way :slight_smile:
The print of the key pressed shows a blank space and the print of the number, of course, zero.
Also this code waits for four keys to show the string and the total.
If I change the line:

 int myInt = key;

for:

 int myInt = key-'0';

The key printed still is the blank space and the number -48. So the problem is that doesn´t recognizes the keypad in this code... But recognizes in the other codes I have made...

¿What is wrong?

Cheers

K, I remember having a similar issue when I did a keypad program

so instead of changing to int
I did Case statements

switch(key)
case'0'
case'1'
case'2'
case'3'

I know this is probably not the easiest way, but I remember it being a problem. but with this way you can use the * and # keys or choose to ignore them

the other command I think worked was

int buttonkey = 0;
buttonkey = key.toInt();
Serial.print(buttonkey);
if(keypad.getKey()!= NO_KEY);
{                
 char key = keypad.getKey();

Why do you need to read the key AGAIN AND AGAIN? key HAD a value when the while loop ended, or it would have if you'd used my code.. DO NOT CREATE ANOTHER VARIABLE CALLED KEY!!!

Hello,
Your code...

while(key = keyPad.getKey() == NO_KEY)
{
    // Do nothing
}
// Use key

Doesn't compiles... gives an error that now I don't remeber. But when I change to:

while(keypad.getKey() == NO_KEY)
{
    // Do nothing
}
if(keypad.getKey()!= NO_KEY);
{                
 char key = keypad.getKey();
 int myInt = key;
 Serial.println("You have pressed ");
 Serial.print(key);
 Serial.println("The corresponding number is ");
 Serial.print(myInt);
 myNum[i]= myInt;
 i = i+1;
}

You can check that even I have left the "// Do nothing" of your code... But now compiles...
And is giving back "0000"
I don't see where I'm calling key so many times as you say. First I read as a character the keypad is giving me, and the change to a number int temporary, and with this temporary int, create the differente digits of the number... But only reading the key once.
Explain better my fail or better your code...

Cheers

Try this one. Study it. Understand it. Then write something like it for your own use.

#include <Keypad.h>

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

//define the symbols
char stdKeys[ROWS][COLS] = {
  { '1' , '2' , '3' , 'A' },
  { '4' , '5' , '6' , 'B' },
  { '7', ' 8', ' 9', ' C' },
  { '#' , '0' , '*' , 'D'}
};
byte colPins[ROWS] = {2, 3, 4, 5}; //connect to the row pinouts of the keypad
byte rowPins[COLS] = {6, 7, 8, 9}; //connect to the column pinouts of the keypad
int myInt = 0;
byte digitCount = 0;

//initialize an instance of class NewKeypad
Keypad customKeypad = Keypad( makeKeymap(stdKeys), rowPins, colPins, ROWS, COLS); 

void setup(){
  Serial.begin(115200);
  Serial.println("Enter a 4 digit number");
}

void loop(){
  char Key = customKeypad.getKey();

  if (Key >= '0' && Key <= '9'){
    Serial.println(Key);
      myInt = (myInt * 10) + Key -'0';
      digitCount++;
    if (digitCount == 4) {
      Serial.print("You entered: ");
      Serial.println(myInt);
      digitCount = 0;
      myInt = 0;
    }
  }
}

Hello lar3ry,
It works really fine, thank you very much :slight_smile:
Very interesting the way you make the four digit number by iteration multiplication :astonished:
Hope this code will not give me problems in the final program, I have to enter five 4-digit numbers that go to really different parts of the program...
Anyway I'm really pleased :slight_smile:

Cheers

Amalitus_I:
It works really fine, thank you very much :slight_smile:
Very interesting the way you make the four digit number by iteration multiplication :astonished:
Hope this code will not give me problems in the final program, I have to enter five 4-digit numbers that go to really different parts of the program...
Anyway I'm really pleased :slight_smile:

There are a number of ways to do that. The brute-force method would be to duplicate the code 5 times, using another variable to select each code chunk in turn, which would work, but would be ugly and ineffifient.

A few ideas for you.

  1. Place that code in a function that returns an int. You'll need to make your own loop within the function. When you have the desired number of digits entered, you return the int. In this way, you can call the function for any variable by using something like
int frabble = getInt();  // where getInt is the function
int dwibb = getInt();  // and so on
  1. You could enclose the code within a for loop that will cause you to execute an inner loop 5 times, and assign the result of the inner loop to 5 elements of an array of ints.

Another thought for you. about the function. You could make it with two parameters, like this:

int getInt ( char size, bool method ) {
   // your code goes here
}

Now, when you call it, you give it a char containing the number of characters you want to enter for the int, and set method to true. Or, you could give it a char containing a character to use as a delimiter (end of input), and set method to false.

flabber = getInt ( 4, true );
gribble = getInt ( 'A', false );

Of course you then need to look for the delimiter to decide when to return the int.

There's a rather nice site for learning more abut these concepts at http://www.learncpp.com/

Keypad code adapted from http://howtomechatronics.com/projects/arduino-security-alarm-system-project/

I modified it to accept 1, 2, 3 or 4 digits and return that value so it can be used in a variable for driving servo motors to a specific feedback voltage.

Hope this helps someone!

Plank

char keypressed;
int tempFeedback = 0;
int value0;
int value1;
int value2;
int value3;
long val;
long readVal;
bool activated;
const byte ROWS = 4; //--(four rows)--
const byte COLS = 4; //--(four columns)--
char keys[ROWS][COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'}, //--(define the symbols on the buttons of the keypad)--
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};
byte rowPins[ROWS] = {26, 27, 28, 29};
byte colPins[COLS] = {30, 31, 32, 33};

Keypad kpd = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );  //--(Initialize an instance of class NewKeypad)--

/*
  _  _______   _____  _   ___  
 | |/ / __\ \ / / _ \/_\ |   \ 
 | ' <| _| \ V /|  _/ _ \| |) |
 |_|\_\___| |_| |_|/_/ \_\___/ 

int atoi(const char* string);  Convert string to int
long atol(const char* string);  Convert string to long
double atof(const char* string);  Convert string to double
long double atolf(const char* string);  Convert string to long double
 */
 
//--(keypad)--
int GetNumber() //--(Function for 1 to 4 digit number with return value int)--
{
   int k=10; //--(local variable)--
   static char buffer1[4]; //--(Used to store char string variable for atoi conversion)--
   tempFeedback = 0; //--(Reset any previous held value for variable to zero)--
   activated = true; //--(flag variable for while loop below)--
   lcd.clear();
   lcd.setCursor(0,0);
   lcd.print("*** Keypad Enter ***");
   lcd.setCursor(0,1);
   lcd.print("Feedback>");
   lcd.setCursor(0, 2);
   lcd.print("# = Restart");
   lcd.setCursor(0,3);
   lcd.print("* = Enter");
     while(activated) 
     {
        keypressed = kpd.getKey();
        if (keypressed != NO_KEY)
        {
           if (keypressed == '0' || keypressed == '1' || keypressed == '2' || keypressed == '3' ||
              keypressed == '4' || keypressed == '5' || keypressed == '6' || keypressed == '7' ||
              keypressed == '8' || keypressed == '9' ) 
           {
              tempFeedback += keypressed;
              lcd.setCursor(k,1); //--(Place digit at display location i)--
              lcd.print(keypressed); //--(Print digit)--
              buffer1[i] = keypressed; //--(Put keypressed string in buffer1 for processing conversion to float)--
              if (k == 10)
              {
                 value0 = atoi(buffer1); //--(Convert string to int and put result in value0)--
              }
              
              if (k == 11)
              {
                 value1 = atoi(buffer1); //--(Convert string to int and put result in value1)--
              }
              
              if (k == 12)
              {
                 value2 = atoi(buffer1); //--(Convert string to int and put result in value2)--
              }
              
              if (k == 13)
              {
                 value3 = atoi(buffer1); //--(Convert string to int and put result in value3)--
              }
              
              k++;
           }
          
        }
        
        if (k > 14 || keypressed == '#') //--(Reset keypad entry)--
        {
           tempFeedback = 0;
           k=10;
           lcd.clear();
           lcd.setCursor(0,0);
           lcd.print("*** Keypad Enter ***");
           lcd.setCursor(0,1);
           lcd.print("Feedback>");
           lcd.setCursor(0, 2);
           lcd.print("# = Restart");
           lcd.setCursor(0,3);
           lcd.print("* = Enter");
        }
        
        if ( keypressed == '*') //--(ACTIVATE SERVO TO GO TO temFeedback position)--
        {
           activated = false; //--(flag variable to stop while loop and continue)--
           delay(4000);
           if (k == 14)
           {
              tempFeedback = value0 * 1000 + value1 * 100 + value2 * 10 + value3; //--(Construct complete 4 digit number)--
           }

           else if (k == 13)
           {
              tempFeedback = value0 * 100 + value1 * 10 + value2; //--(Construct complete 3 digit number)--
           }

           else if (k == 12)
           {
              tempFeedback = value0 * 10 + value1; //--(Construct complete 2 digit number)--
           }

           else if (k == 11)
           {
              tempFeedback = value0; //--(Construct complete 1 digit number)--
           }
                    
        } 
        
     }
     return tempFeedback; //--(This variable is the product of this function and is returned to the function call statement)--
}