Adafruit 1x4 Membrane Keypad Help

Hi

I’m currently running an arduino nano, adafruit 14 segment alphanumeric display and the mentioned keypad. The concept is that I have 4 options I want to display on the 14 segment display via the keypad. I am able to display whatever I want on it but I’m having issues with getting the keypad function to work.


#include <Wire.h>
#include <Adafruit_GFX.h>
#include "Adafruit_LEDBackpack.h"


Adafruit_AlphaNum4 alpha4 = Adafruit_AlphaNum4();

void setup() {
  
  Serial.begin(9600);
  alpha4.begin(0x70);  // pass in the address
  char displaybuffer[4] = {' ', ' ', ' ', ' '};
  alpha4.setBrightness(1); // brightness range 0 - 15
}
void loop() {
  alpha4.writeDigitAscii(1, '1'); // address range is 0 - 3, 0 being first digit from left
  alpha4.writeDigitAscii(2, '2');
  alpha4.writeDigitAscii(3, '0');
  alpha4.writeDisplay();

}

I’ve read about debouncing and I’m feeling that I’ll need to consider that in this approach but I’ve only ever seen anything regarding a singular button.

I’m also not sure how to have the program read each button for input continuously, I have seen a for loop but I had some funky results (possibly due to debouncing?).

Appreciate the help, still learning arduino so I’m hoping to learn a whole bunch from this.

2 Likes

i am no master but do u have missing librarys,???

2 Likes

For what it does the code works which is just to simply throw a predefined set of characters on the segment display.

2 Likes

Hi Jake,

Welcome to the forums!

I’d recommend using an interrupt to call an appropriate function when a key is presseed. Checkout chapter 5.4 of our Arduino Beginner’s Workshop:

2 Likes

Hi

This is what I have so far and it is good to go.

const int buttonPin[] = {3,4,5,6};     // the number of the pushbutton pins
const int goPin = 10;
const int ledPin =  13;      // the number of the LED pin
// variable for reading the pushbutton status
#include <Wire.h>
#include <Adafruit_GFX.h>
#include "Adafruit_LEDBackpack.h"
Adafruit_AlphaNum4 alpha4 = Adafruit_AlphaNum4();

void setup() {
  // initialize the LED pin as an output:
  pinMode(ledPin, OUTPUT);
  pinMode(goPin, INPUT_PULLUP); 
  
  Serial.begin(9600); // initialize the Serial Monitor @ 9600
  alpha4.begin(0x70);  // pass in the address
  char displaybuffer[4] = {' ', ' ', ' ', ' '};
  alpha4.setBrightness(1); // brightness range 0 - 15  
  
  for(int x=0; x<4; x++) // initialize the keypad pin(s) as an input:
  {
    pinMode(buttonPin[x], INPUT_PULLUP); 
  }  
}

void loop(){
  // read the state of the keypad value:
  for(int x=0; x<4; x++)
  {
    //signifying the state of which the button is in by reading the appropriate pin #
    buttonState = digitalRead(buttonPin[x]);

    // check if the pushbutton on the keypad is pressed.
    // if it is, the buttonState is LOW:
    // BUTTON 3
    if (buttonState == LOW && buttonPin[x] == 3) {    
      alpha4.clear();   
      alpha4.writeDigitAscii(1, '1'); // address range is 0 - 3, 0 being first digit from left
      alpha4.writeDigitAscii(2, '8');
      alpha4.writeDigitAscii(3, '0');
      alpha4.writeDisplay();
    }
    // BUTTON 4
    if (buttonState == LOW && buttonPin[x] == 4) {
      alpha4.clear();
      alpha4.writeDigitAscii(1, '2'); // address range is 0 - 3, 0 being first digit from left
      alpha4.writeDigitAscii(2, '4');
      alpha4.writeDigitAscii(3, '0');
      alpha4.writeDisplay();
    }
    // BUTTON 1
    if (buttonState == LOW && buttonPin[x] == 5) {
      alpha4.writeDigitAscii(0, 'L'); // address range is 0 - 3, 0 being first digit from left
      alpha4.writeDigitAscii(1, 'I');
      alpha4.writeDigitAscii(2, 'N');
      alpha4.writeDigitAscii(3, 'E');
      alpha4.writeDisplay();
    }
    // BUTTON 2
    if (buttonState == LOW && buttonPin[x] == 6) {
      alpha4.clear();
      alpha4.writeDigitAscii(1, '1'); // address range is 0 - 3, 0 being first digit from left
      alpha4.writeDigitAscii(2, '2');
      alpha4.writeDigitAscii(3, '0');
      alpha4.writeDisplay();
    }
  }
}

The next step is to be able to trigger a delay based on the values on the display (value may change later when I determine final delay timings) and operate a transistor to enable power flow to a solenoid.

So something like this:

  • User presses button 2 which is 120 sec
  • Program stores this option
  • User presses an initiate button that after the delay sets a pin high to turn on the transistor.

I feel like I’m not far off from getting it done :S

1 Like

I’d strongly recommend rewriting it to use interrupts, but using your current code you could modify it slightly like this:

const int buttonPin[] = {3,4,5,6};     // the number of the pushbutton pins
const int goPin = 10;
const int ledPin =  13;      // the number of the LED pin
int delayt = 120; //initialise delayt, default to 120s
// variable for reading the pushbutton status
#include <Wire.h>
#include <Adafruit_GFX.h>
#include "Adafruit_LEDBackpack.h"
Adafruit_AlphaNum4 alpha4 = Adafruit_AlphaNum4();

void setup() {
  // initialize the LED pin as an output:
  pinMode(ledPin, OUTPUT);
  pinMode(goPin, INPUT_PULLUP); 
  
  Serial.begin(9600); // initialize the Serial Monitor @ 9600
  alpha4.begin(0x70);  // pass in the address
  char displaybuffer[4] = {' ', ' ', ' ', ' '};
  alpha4.setBrightness(1); // brightness range 0 - 15  
  
  for(int x=0; x<4; x++) // initialize the keypad pin(s) as an input:
  {
    pinMode(buttonPin[x], INPUT_PULLUP); 
  }  
}

void loop(){
  // read the state of the keypad value:
  for(int x=0; x<4; x++)
  {
    //signifying the state of which the button is in by reading the appropriate pin #
    buttonState = digitalRead(buttonPin[x]);

    // check if the pushbutton on the keypad is pressed.
    // if it is, the buttonState is LOW:
    // BUTTON 3
    if (buttonState == LOW && buttonPin[x] == 3) {    
      delayt=180;
      alpha4.clear();   
      alpha4.writeDigitAscii(1, '1'); // address range is 0 - 3, 0 being first digit from left
      alpha4.writeDigitAscii(2, '8');
      alpha4.writeDigitAscii(3, '0');
      alpha4.writeDisplay();
    }
    // BUTTON 4
    if (buttonState == LOW && buttonPin[x] == 4) {
      delayt=240;
      alpha4.clear();
      alpha4.writeDigitAscii(1, '2'); // address range is 0 - 3, 0 being first digit from left
      alpha4.writeDigitAscii(2, '4');
      alpha4.writeDigitAscii(3, '0');
      alpha4.writeDisplay();
    }
    // BUTTON 1
    if (buttonState == LOW && buttonPin[x] == 5) {
      alpha4.writeDigitAscii(0, 'L'); // address range is 0 - 3, 0 being first digit from left
      alpha4.writeDigitAscii(1, 'I');
      alpha4.writeDigitAscii(2, 'N');
      alpha4.writeDigitAscii(3, 'E');
      alpha4.writeDisplay();
      
      //DELAY
      digitalWrite(ledPin, HIGH)
      delay(delayt*1000);
      digitalWrite(ledPin, LOW)
    }
    // BUTTON 2
    if (buttonState == LOW && buttonPin[x] == 6) {
      delayt=120;
      alpha4.clear();
      alpha4.writeDigitAscii(1, '1'); // address range is 0 - 3, 0 being first digit from left
      alpha4.writeDigitAscii(2, '2');
      alpha4.writeDigitAscii(3, '0');
      alpha4.writeDisplay();
    }
  }
}

I haven’t actually checked to see if this compiles, but I think it should work.

Ok thanks. I might have to look more into writing interrupts like you’ve mentioned above. The other big issue I see using my current method is how to make the program wait for a button input when Button 1 is pressed, the rest will be a delay besides this one.

Yeah, if using interrupts you’d be able to abort the delay. As it is, once the button is entered to trigger the delay your program will appear to freeze for the full length of the delay.

Won’t I have an issue using interrupts if I’m running a Nano? Will I need to look at a micro instead?

And just so I can ensure I’m getting interrupts correct, a specific interrupt will be linked to each button and then run the program linked to that interrupt?

I’m fairly sure that the Nano uses the same pins for interrupts as an Arduino Uno on pins 2 and 3 so that shouldn’t cause an issue. You can set it up that way yes, however, if you’re unsure, I’d recommend reading through the documentation from Arduino directly first as they cover that in much better detail than I can.

Hi Jake,

There are no issues with running interrupts on an Arduino Nano. They use the same ATMega 328P Microcontroller as the full-size Uno R3.

It’ll depend how you want to set it up, but best practice is to do the absolute minimum in the interrupt function.

My suggestion would be to setup an interrupt that is triggered when any button is pressed, and contains a function to check which button has been pushed, then sets a global int variable with the button number.

Then once that’s completed your code which is running all the time can check that variable, and if it’s not eg -1, take some action then reset the value to -1 once the action is completed.

From that link above Nano only has 2 interrupt pins, seeing as I’ll have 4 keypad buttons and an initiate button I’ll probably need to look at a board that supports more interrupts? Something like the Micro? The aim to keep this as small as possible within the realm of still doing its job.

I’m glad that some of my thoughts on how to do the interrupts align with yours haha. Hopefully after today I’ll have that sorted.

Hey Jake,

That’s correct, there are some hacky tricks I can think of manipulating multiplexing to be able to get away with reading 4 buttons from 2 interrupts, but that will likely just add extra unneeded complexity to the project. I’d say you’re dead on, I’m quite sure the Arduino Micro has 5 interrupt pins so that should be suitable and easy to integrate with your current project.

Cheers Bryce, I’ll substitute my mega in for now and then get work to buy the other one. At least it is works money :wink:

Haha fair enough Jake!

Let us know how you go with it. :grin:

Yep a Mega would be the simpler way to go conecptually, but it’s definitely achievable with an uno or Nano - you’d just tie all your buttons to both the interrupt pin and another GPIO so that any button would trip the interrupt, and then your interrupt code could determine which button it was.

1 Like

Ah I see what you mean Oliver, trip the interrupt and run a digitalread for the button press. Hmm. Turn around for another board could be a while so that may be the better option.

3 Likes

Ok this is what I have so far:

int buttonPinOne = 0;
int buttonPinTwo = 0;
int buttonPinThree = 0;
int buttonPinFour = 0;    // the number of the pushbutton pins
int optionSelected = 0;
int buttonState = 0;
const int goPin = 10;
const int ledPin =  13;      // the number of the LED pin

// variable for reading the pushbutton status
#include <Wire.h>
#include <Adafruit_GFX.h>
#include "Adafruit_LEDBackpack.h"
Adafruit_AlphaNum4 alpha4 = Adafruit_AlphaNum4();


// Interrupt Service Routine (ISR)
void keypadPress (){
    alpha4.clear();
    buttonState = digitalRead(2);
    buttonPinOne = digitalRead(4);
    buttonPinTwo = digitalRead(5);
    buttonPinThree = digitalRead(6);
    buttonPinFour = digitalRead(7);
    optionSelected = 1;
}

void setup() {
  pinMode(ledPin, OUTPUT);
  pinMode(goPin, INPUT_PULLUP); 
  pinMode(buttonPinOne, INPUT_PULLUP);
  pinMode(buttonPinTwo, INPUT_PULLUP);
  pinMode(buttonPinThree, INPUT_PULLUP);
  pinMode(buttonPinFour, INPUT_PULLUP);
  
  Serial.begin(9600); // initialize the Serial Monitor @ 9600
  alpha4.begin(0x70);  // pass in the address
  char displaybuffer[4] = {' ', ' ', ' ', ' '};
  alpha4.setBrightness(1); // brightness range 0 - 15  
  attachInterrupt (digitalPinToInterrupt (2), keypadPress, CHANGE); 
 
}

void loop(){

    if (buttonState == LOW && buttonPinOne == LOW) {    
      alpha4.writeDigitAscii(0, 'L'); // address range is 0 - 3, 0 being first digit from left
      alpha4.writeDigitAscii(1, 'I');
      alpha4.writeDigitAscii(2, 'N');
      alpha4.writeDigitAscii(3, 'E');
      alpha4.writeDisplay();
    }

    if (buttonState == LOW && buttonPinTwo == LOW) {
      alpha4.writeDigitAscii(0, '0'); // address range is 0 - 3, 0 being first digit from left
      alpha4.writeDigitAscii(1, '0');
      alpha4.writeDigitAscii(2, '6');
      alpha4.writeDigitAscii(3, '0');
      alpha4.writeDisplay();
    }

    if (buttonState == LOW && buttonPinThree == LOW) {
      alpha4.writeDigitAscii(0, '0'); // address range is 0 - 3, 0 being first digit from left
      alpha4.writeDigitAscii(1, '1');
      alpha4.writeDigitAscii(2, '2');
      alpha4.writeDigitAscii(3, '0');
      alpha4.writeDisplay();
    }

    if (buttonState == LOW && buttonPinFour == LOW) {
      alpha4.writeDigitAscii(0, '0'); // address range is 0 - 3, 0 being first digit from left
      alpha4.writeDigitAscii(1, '1');
      alpha4.writeDigitAscii(2, '8');
      alpha4.writeDigitAscii(3, '0');
      alpha4.writeDisplay();
    }
  }

It compiles ok but I’m unable to test it yet (at home).

Next thing I do is I need to have another interrupt that is triggered by a ‘start’ button. I also need to include a delay till a pin goes high to enable a solenoid (not power it). I’m thinking each button press and its resulting display will also set a a variable for use with the start button. I’m leaning towards doing some if statements within the ISR such as ‘if optionSelected == 1 && selectionOne == 1’ but from what I read it is best to keep the ISR short. The other problem I see is for the LINE selection where I’m waiting on an externally connected button to ground a pin on the nano and trigger the device but that may be as simple as just taking that function out of the button and display functions and make it a simple if pin x goes low enable solenoid. If I can incorporate that into the keypad press it would be nice, almost like it needs to loop constantly to see if this goes low.

1 Like
Oh No.

Not the result I was hoping for :S

I suspect a wiring issue - such that every button pulls every gpio button pin low - or insufficient pull-down resistance and something is shorted to ground.