Storing a variable using a 4x4 button matrix

Hey everyone,

I want to thank the people that take their time help me out with my issue. I have a 4x4 button matrix hooked up and I want to make it output strings instead of characters. My first question is, is this possible and is there any existing code that will help me out?

My second question is, would it be possible to store an output of the button matrix to be then printed out when a simple button is pressed.

So it would go, a button on the button matrix is pressed the output is stored as a variable. Then when the simple button is pressed the output would be printed. This is what I have so far.

#include <Keypad.h>

const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns
const char key[ROWS][COLS] = {
  {"ELUM  AO","ROMU  MFR","LOMA6  BHS","LOJA  JHS"},
  {"ALPE4  GM","CEOR7  OBS","MIVI  JSG","EUFO5  WC"},
  {"EUAL13  WBB","PYCA80  CP","DIOP  CY","SL  "},
  {"  TOH","Other 1","Other 2","Other 3"}
};

byte rowPins[ROWS] = {4, 5, 6, 7}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {11, 10, 9, 8}; //connect to the column pinouts of the keypad

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

const int buttonPin = 12;
int ledPin = 13;
int buttonState = 0;

void setup(){
  Serial.begin(9600);
  pinMode(buttonPin, INPUT);
  pinMode(ledPin, OUTPUT);
}

void loop(){
buttonState = digitalRead(buttonPin);
   const char key = keypad.getKey(); 
     if (buttonState == LOW){       
       digitalWrite(ledPin, LOW);
       }

  
      if (buttonState == HIGH){
       digitalWrite(ledPin, HIGH);       
       Serial.println(key);
       return;
       }       
  }

I'm a newbie so as much explanation as you have time for would be appreciated. Thanks!

The easy, crude way would be to use the keypad code the way it's usually done, then store your strings in a separate array, perhaps in flash memory using PROGMEM, and use the key presses as an index into the array to retrieve the actual char strings. (strings, not Strings)

Okay I will research how to do that. Thanks!

You said that is the "easy crude way" is there another way that wouldn't be considered crude?

You would need to modify the keypad library to do it in a more elegant way, but it could be done in such a way that the char arrays could be used directly.
I'm not familiar enough with the keypad library to tell you exactly how to do it - you'd need to spend some time studying the library and how it works, then modify it to suit your purpose, or write your own library.
Probably better to do it the way I suggested, unless you plan to use it like this all of the time. :slight_smile:

Steve,

I have the button matrix printing strings with this code.

#include <Keypad.h>

String invaSpe[17] = {"ELUM  AO","ROMU  MFR","LOMA6  BHS","LOJA  JHS","ALPE4  GM","CEOR7  OBS","MIVI  JSG","EUFO5  WC","EUAL13  WBB","PYCA80  CP","DIOP  CY","SL","TOH","Other 1","Other 2","Other 3"};
const byte ROWS = 4; //four rows
const byte COLS = 4; //three columns
const char key[ROWS][COLS] = {
  {'1','2','3','4'},
  {'5','6','7','8'},
  {'9','a','b','c'},
  {'d','e','f','g'}
};

byte rowPins[ROWS] = {4, 5, 6, 7}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {11, 10, 9, 8}; //connect to the column pinouts of the keypad

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

const int buttonPin = 12;
int ledPin = 13;
int buttonState = 0;

String stringInvaSpe;

void setup(){
  Serial.begin(9600);
  pinMode(buttonPin, INPUT);
  pinMode(ledPin, OUTPUT);
}

void loop(){
  const char key = keypad.getKey(); 
    if (key == '1'){
      stringInvaSpe = invaSpe[0];
      //Serial.println(invaSpe[0]);
    }
    if (key == '2'){
      stringInvaSpe = invaSpe[1];
      //Serial.println(invaSpe[1]);
    }
    if (key == '3'){
      stringInvaSpe = invaSpe[2];
      //Serial.println(invaSpe[2]);
    }
    if (key == '4'){
      stringInvaSpe = invaSpe[3];
      //Serial.println(invaSpe[3]);
    }
    if (key == '5'){
      stringInvaSpe = invaSpe[4];
      //Serial.println(invaSpe[4]);
    }
    if (key == '6'){
      stringInvaSpe = invaSpe[5];
      //Serial.println(invaSpe[5]);
    }
    if (key == '7'){
      stringInvaSpe = invaSpe[6];
      //Serial.println(invaSpe[6]);
    }
    if (key == '8'){
      stringInvaSpe = invaSpe[7];
      //Serial.println(invaSpe[7]);
    }
    if (key == '9'){
      stringInvaSpe = invaSpe[8];
      //Serial.println(invaSpe[8]);
    }
    if (key == 'a'){
      stringInvaSpe = invaSpe[9];
      //Serial.println(invaSpe[9]);
    }
    if (key == 'b'){
      stringInvaSpe = invaSpe[10];
      //Serial.println(invaSpe[10]);
    }
    if (key == 'c'){
      stringInvaSpe = invaSpe[11];
      //Serial.println(invaSpe[11]);
    }
    if (key == 'd'){
      stringInvaSpe = invaSpe[12];
      //Serial.println(invaSpe[12]);
    }
    if (key == 'e'){
      stringInvaSpe = invaSpe[13];
      //Serial.println(invaSpe[13]);
    }
    if (key == 'f'){
      stringInvaSpe = invaSpe[14];
      //Serial.println(invaSpe[14]);
    }
    if (key == 'g'){
      stringInvaSpe = invaSpe[15];
      //Serial.println(invaSpe[15]);
    }

buttonState = digitalRead(buttonPin);    
     if (buttonState == HIGH){       
       digitalWrite(ledPin, LOW);
     }

     if (buttonState == LOW){
       digitalWrite(ledPin, HIGH);       
       Serial.println(stringInvaSpe);
       return;
      }
       
}

Is that what you had in mind?

I'm still having trouble with the simple button. Do you have any ideas on that?

would it be possible to store an output of the button matrix to be then printed out when a simple button is pressed.

Sort of. Rather than all of the 'if' statements, 'switch case' might be better.
Even better still, you could possibly even use consecutive byte values for the values in the key array, then use them as a direct index into your array without the 'if' or 'switch case' at all.

So the key array would look more like this:-

const char key[ROWS][COLS] =
{
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 10, 11, 12},
    {13, 14, 15, 16}
};

You can't use 0, because that's the result that getKey() returns if there is no key pressed. ('\0' actually)
You only take action if 'getKey()' returns a value >0.
Then, you could flag whether or not a key was pressed with "gotKey=true" or similar, and after that subtract 1 from the returned key value, leaving you with a value between 0 and 15.
That would be the index into your array of Strings. (They should ideally be char strings too, not 'String' objects, but we'll ignore that for now.)

Then, when the pushbutton is pressed, if 'gotKey' is true, you would print the String using "Serial.println(invaSpe[key]);", then make 'gotKey' false, ready for the next key press from the keypad.

This shouldn't be declared as "const" either:-

const char key = keypad.getKey();

It should just be:-

char key = keypad.getKey();

I hope you understand what I mean.
I'd need a keypad set up here to write and test it myself, to be sure it was going to work OK. And some casting between char and byte might be needed. I'm not sure on that point. I think it would work fine with char though. As mentioned in an earlier post, I'm not too familiar with the "Keypad" library. I've never used it (yet).

This is what I'm talking about, but it would need to be tested to see if it works with the library and that 'getKey()' returns the value correctly:-
(And since it's untested, it might have bugs, but shows what I mean at least. I hope I haven't overlooked anything too serious. :slight_smile: )

#include <Keypad.h>

const byte ROWS = 4;                            // Four rows
const byte COLS = 4;                            // Four columns
//const char key[ROWS][COLS] =
const byte key[ROWS][COLS] =
{
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 10, 11, 12},
    {13, 14, 15, 16}
};
const byte buttonPin = 12;
const byte ledPin = 13;
const String invaSpe[16] = {"ELUM  AO", "ROMU  MFR", "LOMA6  BHS", "LOJA  JHS", "ALPE4  GM", "CEOR7  OBS", "MIVI  JSG", "EUFO5  WC", "EUAL13  WBB", "PYCA80  CP", "DIOP  CY", "SL", "TOH", "Other 1", "Other 2", "Other 3"};
byte rowPins[ROWS] = {4, 5, 6, 7};              // Connect to the row pinouts of the keypad
byte colPins[COLS] = {11, 10, 9, 8};            // Connect to the column pinouts of the keypad
bool gotKey = false;

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

void setup()
{
    Serial.begin(9600);
    pinMode(buttonPin, INPUT);
    pinMode(ledPin, OUTPUT);
}

void loop()
{
//    char key = keypad.getKey();
    byte key = (byte)keypad.getKey();
    if (key)
    {
        gotKey = true;
        key--;                                  // Convert to index for String array
    }
    if (digitalRead(buttonPin) == LOW && gotKey == true)
    {
        digitalWrite(ledPin, HIGH);             // Light LED, but it will go out almost immediately
        Serial.println(invaSpe[key]);
        key = 0;
        gotKey = false;
    }
    else
        digitalWrite(ledPin, LOW);              // The LED needs 'millis()' - based timing to stay on longer!
}

Thank you Steve for putting your time into this! The code I came with and the code you gave me both only returned the first item in the array. Do you have any ideas why that is? This is what I came up with, with your suggestions.

#include <Keypad.h>

String invaSpe[16] = {"ELUM AO","ROMU MFR","LOMA6 BHS","LOJA JHS","ALPE4 GM","CEOR7 OBS","MIVI JSG","EUFO5 WC","EUAL13 WBB","PYCA80 CP","DIOP CY","SL","TOH","Other 1","Other 2","Other 3"};
const byte ROWS = 4; //four rows
const byte COLS = 4; //three columns
const char key[ROWS][COLS] = {
{1,2,3,4},
{5,6,7,8},
{9,10,11,12},
{13,14,15,16}
};

byte rowPins[ROWS] = {4, 5, 6, 7}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {11, 10, 9, 8}; //connect to the column pinouts of the keypad

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

int buttonPin = 12;
int ledPin = 13;
int buttonState = 0;

//String stringInvaSpe;
boolean isSpraying = true;

void setup(){
Serial.begin(9600);
pinMode(buttonPin, INPUT);
pinMode(ledPin, OUTPUT);
}

void loop(){
char key = keypad.getKey();

buttonState = digitalRead(buttonPin);
if (buttonState == HIGH && isSpraying == true){
isSpraying = false;
digitalWrite(ledPin, LOW);
}

if (buttonState == LOW && isSpraying == false){
isSpraying = true;
digitalWrite(ledPin, HIGH);
Serial.println(invaSpe[key]);
return;
}
}

Why didn't you use code tags? You can't post your code inline, and you obviously already know that!
Don't make it harder for people that might want to help.

As I said, the method I suggested might not work hand-in-hand with the keypad library. I've never used it, and don't have a keypad set up to try it, so that's why I suggested you try and see what happens. It was worth a try, but if you tried my code as written, then obviously the library won't accept that method. Pity, it would have made things easier for you.

And you've now made other changes that will stop the code working correctly. With your changes, when you press the button, the last value returned by 'getKey()' will always be 0, indicating that no key has been pressed, so of course you only get the first value in the array.

And since you no longer modify the value returned by 'getKey()', even if you fix that, and the library does return a value between 0 and 15, then you'll never get the first String.

As you work on it, you could add 'Serial.print()' in some places for debugging.

Sorry I didn't realize that was such a big deal. It's noted and I won't do it again. I was editing it after I posted it and the button to insert code wasn't there. Again I am new at this and when I say new I mean I have only been at this for 3 month and I have no previous coding experience. Thanks again.

OK, I just quickly set up a 4x4 keypad and corrected the code I posted yesterday.
I overlooked something, but it's fixed now and works well.

I also made changed this line from "INPUT" to "INPUT_PULLUP".
If you don't have an external pullup resistor, you need it. (If you do have an external pullup resistor, change it back to "INPUT".):-

pinMode(buttonPin, INPUT_PULLUP);

Here's the working (and tested) code:-

// KeypadTest.ino

#include <Keypad.h>

const byte ROWS = 4;                            // Four rows
const byte COLS = 4;                            // Four columns
const byte key[ROWS][COLS] =
{
    {1, 2, 3, 4},
    {5, 6, 7, 8},
    {9, 10, 11, 12},
    {13, 14, 15, 16}
};
const byte buttonPin = 12;
const byte ledPin = 13;
const String invaSpe[16] = {"ELUM  AO", "ROMU  MFR", "LOMA6  BHS", "LOJA  JHS", "ALPE4  GM", "CEOR7  OBS", "MIVI  JSG", "EUFO5  WC", "EUAL13  WBB", "PYCA80  CP", "DIOP  CY", "SL", "TOH", "Other 1", "Other 2", "Other 3"};
byte rowPins[ROWS] = {4, 5, 6, 7};              // Connect to the row pinouts of the keypad
byte colPins[COLS] = {11, 10, 9, 8};            // Connect to the column pinouts of the keypad
bool gotKey = false;

byte pressedKey;

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

void setup()
{
    Serial.begin(9600);
    pinMode(buttonPin, INPUT_PULLUP);
    pinMode(ledPin, OUTPUT);
}

void loop()
{
    byte key = (byte)keypad.getKey();
    if (key)
    {
        gotKey = true;
        pressedKey = key - 1;
        key = 0;
    }
    if (digitalRead(buttonPin) == LOW && gotKey == true)
    {
        digitalWrite(ledPin, HIGH);             // Light LED, but it will go out almost immediately
        Serial.println(invaSpe[pressedKey]);
        gotKey = false;
    }
    else
        digitalWrite(ledPin, LOW);              // The LED needs 'millis()' - based timing to stay on longer!
}

Note that the LED only flases for an instant. If you want it to stay on longer, you'll need to add some 'millis()'-based timing as I suggested in the comments.

Here's the results of pressing each key in turn followed by a button press for each, copied from the serial monitor:-

ELUM  AO
ROMU  MFR
LOMA6  BHS
LOJA  JHS
ALPE4  GM
CEOR7  OBS
MIVI  JSG
EUFO5  WC
EUAL13  WBB
PYCA80  CP
DIOP  CY
SL
TOH
Other 1
Other 2
Other 3

Of course, you can delete this commented-out line from the code:-
(I forgot to remove it.)

//const char key[ROWS][COLS] =

Edit: I just edited the code that I posted and removed it, for posterity. :slight_smile:

So, we both learned something - the library does work with numbers from 1 to 15 instead of chars representing the keys.
I thought it would, but it's handy to know for sure, for the future.

So did you get it working?
After I put in all of that effort, I thought you'd at least report on how you went with it.

Hey Steve,

Sorry for the delay. Yeah I got it working. Thanks for all the help!!

Excellent. I'm glad it's working OK.
I just wanted to double-check to make sure you didn't have any problems with it. :slight_smile:

I'm using that method myself at the moment, (returning a value from 0 to 15), for an I2C keypad interface that I'm making using an ATtiny84, so that I use less pins on the UNO whenever I connect the keypad.

Glad you have use for it too!!