Keypad library - digitalRead() not working

I have an lcd, keypad, and a button connected to an arduino mega 2560.

For now forget about the lcd as that works perfectly....lets focus on the keypad and button.

The keypad works just fine when I run the example code. The button also works perfectly fine when I run the example code for it.

So everything will work when coded together....WRONG

Both work independently but when I use the keypad library in conjunction with the digitalRead() for the button it doesn't work. The keypad portion works just fine but the digitalRead seems to do nothing.

I am unable to find anything online where someone else had this exact problem!

My arduino is a 2560 if that makes any difference.

THIS CODE WORKS - THE BUTTON IS OPERATIONAL

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

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(31, 41, 33, 35, 39, 37);

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

int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button


char keys[ROWS][COLS] = {
  {'1','2','3'},
  {'4','5','6'},
  {'7','8','9'},
  {'*','0','#'}
};
byte rowPins[ROWS] = {22, 32, 30, 26}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {24, 53, 44}; //connect to the column pinouts of the keypad

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

void setup() {

  lcd.begin(20, 4);

  lcd.print("Will the button work?");
  
  Serial.begin(9600);

    digitalWrite(52,LOW);
    pinMode(52,INPUT);

}

void loop() {

  
   buttonState = digitalRead(52);
   
   if(buttonState != lastButtonState)
   {
     
     if(buttonState == HIGH){
     
       lcd.print("SWITCH ON");

       
     }else{
       
       lcd.print("SWITCH OFF");
       
     }  
     
   }
   
   lastButtonState = buttonState; 
    
}

THIS CODE DOES NOT WORK - ALL I ADDED WAS THE KEYPAD IF STATEMENT! It starts up, and the lcd prints switch off. Flip the switch on and nothing happens. Type on the keypad and it updates the lcd with keypad characters. It makes no sense.

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

// initialize the library with the numbers of the interface pins
LiquidCrystal lcd(31, 41, 33, 35, 39, 37);

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

int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button


char keys[ROWS][COLS] = {
  {'1','2','3'},
  {'4','5','6'},
  {'7','8','9'},
  {'*','0','#'}
};
byte rowPins[ROWS] = {22, 32, 30, 26}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {24, 53, 44}; //connect to the column pinouts of the keypad

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

void setup() {

  lcd.begin(20, 4);

  lcd.print("Will the button work?");
  
  Serial.begin(9600);

    digitalWrite(52,LOW);
    pinMode(52,INPUT);

}

void loop() {

    char key = keypad.getKey();
  
    if (key){

    lcd.print(key);

    }
  
   buttonState = digitalRead(52);
   
   if(buttonState != lastButtonState)
   {
     
     if(buttonState == HIGH){
     
       lcd.print("SWITCH ON");

       
     }else{
       
       lcd.print("SWITCH OFF");
       
     }  
     
   }
   
   lastButtonState = buttonState; 
    
}

What do you see in your LCD?

I notice that you don't set any position for the strings you are printing. That is strange, since it will keep on printing.

Can you, for the sake of troubleshooting, reserve an area in the LCD where the Switch On and Off will appear? Say in the 0:0 position and only write the letters in the 0:1 ?

Have you tried printing through the serial line what you get from the getKey function?

You've added this:

    char key = keypad.getKey();
  
    if (key){

    lcd.print(key);

    }

Comment all of these and add one by one until you find the one that blocks you. However, I'm still thinking that it may have something to do with the positioning of the cursor.
Also, try to declare the key variable outside of the loop function. Maybe that has something to do with this.

Last but not least, if possible, check the memory usage.

Edit:

I see that you disable the pull up resistor on the input. Is there a reason for that? How have you got everything wired?

It sounds suspiciously like keypad.getKey() blocks till a key is pressed so your call to digitalRead() cannot happen until immediately after pressing a keypad key. The docs for the library say that getKey() doesn't block but the code is hard to read because of the debounce logic - can you check that getKey() is returning?

I was under the impression that the 2560 had internal pull-down resistors but I just went and looked and couldn't find a mention of them in the datasheet. It works perfectly with the example code and has not missed a switch toggle since but I know floating pins are really not a good thing. Basically when the switch is flipped it connects +5v to the pin rather than ground. I'm not sure why I did that but I can fix it tomorrow.

I've done countless things to chase down the bug and it is the call to keypad.getKey() that makes the system stop working. I have commented it out like you said, printed to serial, and disconnected any external devices and it always comes back to that one line of code.

I went through the library a bit but didn't have enough time to understand exactly how it is written. Actually reading the keypad is not that hard so I may end up just writing my own subroutine or class to do it.

The problem has to be in the library somewhere blocking or overriding the digitalRead calls.

In the meantime I think I have an idea. If I do direct port reads via the port register then maybe it can override the library. It may work its worth a shot.

There are no internal pulldowns, only pullups.
If you set pinMode to Input, and then digitlWrite Low, it turns off the pullup.

Well my idea panned out perfectly and I'm now up and running. I fixed the hardware problems - moved switch to ground not vcc (to use internal pull-ups) and moved it to pin 47 because 54 is actually ADC 0. The problem was not caused because it was on ADC 0 because this same problem happened on another digital pin with a different switch.

Basically what I did was read the port register directly which not only worked but is 50x faster than digitalRead(). This allowed me to use the switch and the keypad simultaneously without any problems.

Look at the diagram here

I am using pin 47 which is port PL2

Then I used these two lines to setup the port. The L is the port register in the lines below and the 2 is the pin to read from

 DDRL &= ~(1<<PL2);    //Configure PORTL pin 2 as an input
 PORTL |= (1<<PL2);    //Set the internal pull-up resistor

Then I used this if statement to determine if the button had been pushed. It basically reads the status of pin 2 from the L register and says if its 0 then the switch is on, if its 1 then it is off.

 if((PINL & (1<<PL2)) == 0){
   
   Serial.println("I win you evil software gremlin");
   
 }

So my complete code is this

#include <Keypad.h>

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

int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button

char keys[ROWS][COLS] = {
  {'1','2','3'},
  {'4','5','6'},
  {'7','8','9'},
  {'*','0','#'}
};

byte rowPins[ROWS] = {22, 32, 30, 26}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {24, 53, 44}; //connect to the column pinouts of the keypad

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

void setup() {
 
  Serial.begin(9600);
  
  DDRL &= ~(1<<PL2);    //Configure PORTD pin 2 as an input
  PORTL |= (1<<PL2);    //Set the internal pull-up resistor
    
}

void loop(){
  
    char key = keypad.getKey(); //get the status of the keypad
  
    if (key){ //if a key has been pushed then enter

    Serial.println(key); //print the key that was pushed

    }
  

 if((PINL & (1<<PL2)) == 0){ //if pin PL2 or pin 47 (atmega 2560) is 0 then the switch is on, if it is 1 then it is off.
   
   Serial.println("I win you evil software gremlin");
   
 }
 

  
}

But what exactly was the problem?

You changed a couple of things, but we have no idea what caused this behavior.

The problem resides inside the keypad library. I'm not sure exactly what part does it but after making the call to keypad.getKey() the keypad worked but the digitalreads wouldn't work. I removed the call to keypad.getKey() and bam the digitalreads worked perfectly. This proved that the library is the issue. I have a hunch that the library is utilizing digitalread continuously to sense button presses. I think that in some way this is overriding digitalread and returning nothing when it is called.

What I did is basically a work around but it is much more efficient than digitalread so if anything its an improvement.

I might go and see if I can isolate the software bug later but for now my work around will get me through.

You could probably test this out if you have a 2560, hook up a button then create a keypad on the pins I used. You don't have to hook one up just make sure that its setup to not spit out key-presses. Pushing the keypad buttons didn't cause this calling keypad.getKey() did.

Hi CQB_insomnia,

I am one of the authors of the keypad library and you are correct that the library aggressively grabs the pins on every call. But it should be limited to only the pins you designate for use by the keypad. I did that because I wanted to be able to share (multiplex) those pins with the LCD library. I even talked Lady Ada into doing the same thing with the LCD library. When I looked over your code last night I verified that you were not sharing your button with any of the keypad pins and thought that maybe the other answer you were given would fix your problem.

It looks like I will need to test your code, though. I have the mega2560 with the LCD and keypad hooked up and sharing data pins. I used that setup to rewrite getKey() over the Christmas holidays. It's possible I introduced a bug somewhere. Which version of the keypad library are you using? You can find it inside keypad.h or keypad.cpp. I designated the latest version as 2.0

It is v2.0 according to the .cpp file.

I am about to do some more coding so I might go ahead and use an older library to see if it works.

Here's version 1.8 if you don't already have it.

http://arduino.cc/playground/uploads/Code/keypad

You'll have to rename the file to keypad.zip

I tried your code unmodified and you are right that things aren't working. But the problem seems to be that you are writing your updates off the end of the lcd.

I modified the code to clear and set the lcd to print properly and everything is working perfectly. The only difference is that I left my keypad and lcd connected to my original pins as you'll see in the code below. I used the same pin (52) for the button as you did. If this code doesn't work for you then I will change my pins to match yours and see what happens.

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

// initialize the library with the numbers of the interface pins
//LiquidCrystal lcd(31, 41, 33, 35, 39, 37);
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

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

int buttonState = 0;         // current state of the button
int lastButtonState = 0;     // previous state of the button


char keys[ROWS][COLS] = {
	{'1','2','3'},
	{'4','5','6'},
	{'7','8','9'},
	{'*','0','#'}
};
//byte rowPins[ROWS] = {22, 32, 30, 26}; //connect to the row pinouts of the keypad
byte rowPins[ROWS] = {5, 4, 3, 2}; //connect to the row pinouts of the keypad
//byte colPins[COLS] = {24, 53, 44}; //connect to the column 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 );

void setup()
{
	lcd.begin(20, 4);
	lcd.print("Will the button");
        lcd.setCursor(0,1);
        lcd.print("work?");
        delay(1000);
	
	Serial.begin(9600);
	
	digitalWrite(52,LOW);
	pinMode(52,INPUT);
}

void loop() {
	
	char key = keypad.getKey();
	
	if (key) lcd.print(key);
	
	buttonState = digitalRead(52);
	
	if(buttonState != lastButtonState)
	    if(buttonState == HIGH) 
            {
                lcd.clear();
                lcd.print("SWITCH ON");
            }
	else
            {
                lcd.clear();
                lcd.print("SWITCH OFF");
            }
	
	lastButtonState = buttonState;
}