Matrix Keypad jukebox or vending machine control

The code below is controlling LEDs with a matrix keypad. In the example, pressing one key triggers an action. How could I modify the code to have a sequence of two keys triggering an action. For example a vending machine where pressing A, followed by 1 would activate the chips' servo and A+2 the chocolate bars' servo, etc...

Thanks

void loop()
{
char keypressed = myKeypad.getKey();
if (keypressed != NO_KEY)
if (keypressed)
{
  switch (keypressed)
{
  case '1':
        Serial.println(keypressed);
        digitalWrite(led13, HIGH);
        break;
  case '4':
        Serial.println(keypressed);
        digitalWrite(led13, LOW);
        break;
  case '2':
        Serial.println(keypressed);
        digitalWrite(led12, HIGH);
        break;
if (keypressed != NO_KEY)
if (keypressed)
{

If there is a key pressed, the first if statement will evaluate to true. The second one is useless. ANY key that is pressed will be non-zero, so true.

Will you ALWAYS require two keys? If so, you should be able to determine, when a key is pressed, if that is the first of the pair or the second of the pair.

If not, what triggers the action? That is, how will you distinguish between '1' meaning item 1 should be dispensed and '1' being the first key to select item 12?

tomcat53:
The code below is controlling LEDs with a matrix keypad. In the example, pressing one key triggers an action. How could I modify the code to have a sequence of two keys triggering an action.

If a key is pressed, how do you know if it's the first or second in a sequence ?

You'll need some other variable to count them, like gotKeys. It starts out as zero.

So:
gotKeys= 0;
If a key is pressed
gotKeys= gotKeys+1;

Now you know how many keys you have read, you could write: if( gotKeys == 2 ) and then do something.

Trouble with that is you wouldn't know which keys they were unless you saved them somewhere.

Here you'll need an array to store the keys you got.

char gotKeysArray[8]; // we can remember upto 8 keys

Now every time you get a key, save it in the array.

See if you can figure it out...
I don't want to do all the work :slight_smile:

Yours,
TonyWilk

TonyWilk:
See if you can figure it out...
I don't want to do all the work :slight_smile:

Yours,
TonyWilk

Thanks. Can you give me a bigger push. I have no clue what you are talking about. I wouldn't be able to turn a led on without a sample code, so creating variables and an array is way out of my league!

Thanks again!

tomcat53:
Thanks. Can you give me a bigger push. I have no clue what you are talking about. I wouldn't be able to turn a led on without a sample code, so creating variables and an array is way out of my league!

Thanks again!

Ok, fair enough !

Outside of void loop() you can declare variables which will store a value for as long as you like.
e.g.

  int gotKeys= 0;    // declare an integer variable, initialised with the value 0

the 'If a key is pressed' bit you already have with:

if (keypressed)
{

Ah, we need that array thing... we need to declare an array of keys, which are of type 'char'
which is, again, outside of the void loop():

char gotKeysArray[8];   // we can remember upto 8 keys

To put stuff into or out of an array, we need to tell in which element we want... the index to the array
Indexes to arrays start at 0, so gotKeysArray[0] is the first one, gotKeysArray[1] is the second, and so on.

Right... we can save a key in the array:

if (keypressed)
{
  gotKeysArray[gotKeys]= keypressed;   // save this key
  gotKeys= gotKeys+1;                  // add up how many we have

Now for the slightly more difficult bit...
After we've saved some keys in gotKeysArray(), we can test if we have enough (only 2 in this case)
so... test if we have 2 keys

if( gotKeys == 2 ){

Now we have to decide what 2 keys we need to detect to do something.

Simply we could do:

if( (gotKeysArray[0]= '1') && (gotKeysArray[1]= '2') ){
  // do something when '1' is pressed then '2'
}

Putting it all together:

if (keypressed)
{
  gotKeysArray[gotKeys]= keypressed;   // save this key
  gotKeys= gotKeys+1;                  // add up how many we have
  if( gotKeys == 2 ){
    gotKeys= 0;              
    if( (gotKeysArray[0]= '1') && (gotKeysArray[1]= '2') ){
      // do something when '1' is pressed then '2'
    }
  }

Yours,
TonyWilk

#include <Keypad.h>

const byte numRows= 4; //number of rows on the keypad
const byte numCols= 4; //number of columns on the keypad

//keymap defines the key pressed according to the row and columns just as appears on the keypad
char keymap[numRows][numCols]= 
{
{'1', '2', '3', 'A'}, 
{'4', '5', '6', 'B'}, 
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};

//Code that shows the the keypad connections to the arduino terminals
byte rowPins[numRows] = {9,8,7,6}; //Rows 0 to 3
byte colPins[numCols]= {5,4,3,2}; //Columns 0 to 3

//initializes an instance of the Keypad class
//initializes an instance of the Keypad class
Keypad myKeypad= Keypad(makeKeymap(keymap), rowPins, colPins, numRows, numCols);

// define leds
#define led10 10
#define led11 11
#define led12 12
#define led13 13

int gotKeys= 0;    // declare an integer variable, initialised with the value 0
char gotKeysArray[8];   // we can remember upto 8 keys

void setup()
{
  // initialize digital pins 9, 10, 11, 12 as  outputs
  pinMode(led13, OUTPUT);          // sets the digital pin 9 as output;
  pinMode(led10, OUTPUT);          // sets the digital pin 10 as output;
  pinMode(led11, OUTPUT);          // sets the digital pin 11 as output;
  pinMode(led12, OUTPUT);          // sets the digital pin 12 as output;
 
Serial.begin(9600);
}

void loop()
{
char keypressed = myKeypad.getKey();


    if (keypressed)
{
   gotKeysArray['gotKeys']= keypressed;   // save this key
  gotKeys= gotKeys+1;// add up how many we have
  if( gotKeys == 2 ){
               
    if( (gotKeysArray[0]= 'A') && (gotKeysArray[1]= '1') ){ // do something when 'A' is pressed then '1'
        Serial.println(keypressed);
        digitalWrite(led13, HIGH);
     }
  
}
}
}

This results in any 2 keys being pressed turns on LED on pin 13. Why?

tomcat53:

   gotKeysArray['gotKeys']= keypressed;   // save this key

The variable name gotKeys should not be in quotes.

Yours,
TonyWilk

P.S.
The compiler should really have given you an error for this because it is not allowed in C.
You can have a variable name like gotKeys, you can have a string of characters like "abcd" or "gotKeys", you can have a single character like 'a' or 'b' or '3' but a number of characters in single quotes is not at all valid.

TonyWilk:
The compiler should really have given you an error for this because it is not allowed in C.

Of course it is. Single quotes define a character constant (a char). In that expression the character constant is implicitly cast to int.

(updated after some testing)

The expression is equivalent to this...

  gotKeysArray[0x7973]= keypressed;   // save this key

Thanks for your help. I figured it out differently. Although, it is not very efficient if you have many choices.

I found a code designed for a secret password lock and modified it like this:

#include "Keypad.h"

#define led10 10
#define led12 12
#define led13 13

const byte ROWS = 4; // four rows
const byte COLS = 4; // three columns
char keys[ROWS][COLS] =
{
{'1', '2', '3', 'A'}, 
{'4', '5', '6', 'B'}, 
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};

//Code that shows the the keypad connections to the arduino terminals
byte rowPins[ROWS] = {9,8,7,6}; //Rows 0 to 3
byte colPins[COLS]= {5,4,3,2}; //Columns 0 to 3

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

char A1CODE[2] = {'A','1'}; // selection option 1
char B1CODE[2] = {'B','1'}; // selection option 2
char attempt[2] = {0,0};
int z=0;
void setup()
{
   pinMode(led10, OUTPUT);          // sets the digital pin 10 as output;
   pinMode(led12, OUTPUT);          // sets the digital pin 12 as output;
   pinMode(led13, OUTPUT);          // sets the digital pin 13 as output;
   Serial.begin(9600);
   Serial.println("Enter your selection and press #:");
}
void a1CODE() // do this if the correct KEY is entered
{
   Serial.println(" Thank you...");
   digitalWrite(led13, HIGH);
   digitalWrite(led12, LOW);
   delay(5000);
   digitalWrite(led13, LOW);
   Serial.println("Enter your selection and press #:");
}
void b1CODE() // do this if the correct KEY is entered
{
   Serial.println(" Thank you...");
   digitalWrite(led10, HIGH);
   digitalWrite(led12, LOW);
   delay(5000);
   digitalWrite(led10, LOW);
   Serial.println("Enter your selection and press #:");
}
void incorrectCODE() // do this if an incorrect KEY is entered
{
   Serial.println("Invalid selection");
   digitalWrite(led12, HIGH);
   digitalWrite(led13, LOW);
   delay(2000);
   digitalWrite(led12, LOW);
   Serial.println("Enter your selection and press #:");
   
}
void checkKEY()
{
   int correct=0;
   int i;
   for ( i = 0; i <= 2 ; i++ )
   {
      if (attempt[i]==A1CODE[i])
      {
         correct++;
      }
   }
   if (correct==2)
   {
      a1CODE();
   }
   {
      if (attempt[i]==B1CODE[i])
      {
         correct++;
      }
   }
   if (correct==2)
   {
      b1CODE();
   }
   else
   {
      incorrectCODE();
   }
   for (int zz=0; zz <= 2; zz++) // clear previous key input
   {
      attempt[zz]=0;
   }
}
void readKeypad()
{
   char key = keypad.getKey();
   if (key != NO_KEY)
   {
      switch(key)
      {
      case '*':
         z=0;
         break;
      case '#':
         delay(100); // added debounce
        checkKEY();
         break;
      default:
         attempt[z]=key;
         z++;
      }
   }
}
void loop()
{
   readKeypad();
 }

and it works perfectly fine. Other than the lengthy code if I had, let's say 40 options, do you foresee any other issues?

Shoot! option B1 works ok. However, A1 turns my LED on pin 13 on, but after my delay turns pin12 on as if I had entered a wrong selection. I guess this isn't working after all!

Darn. I tried adding a 3rd option and it all went sideways. Ok... That was a major fail! Back to the drawing board!

Quite.

It is compiler dependent and a really, really, REALLY bad thing.

I'll admit it was a bit of a 'white lie' to say:
"The compiler should really have given you an error for this because it is not allowed in C."

What I should have said is:

In any decent coding environment, multicharacter constant warnings should be #pragma'd to errors and not allowed in any production C (or C++) code.

I had actually forgotten that it was not strictly speaking an error - I think I dimly remember someone with some code which attempted:
unsigned int riff = 'RIFF';

before they were shown the error of their ways.

Yours,
TonyWilk

I can only surmise the effort of changing all of the references of two characters to three characters may have caused the trouble. Trying to help the OP (while challenging myself in the exercise) and condensing the code to an understandable and simple manner, I ran into a block wall. Not wanting to hi-jack the thread, I believe we could both learn in the process.

char* A1CODE[2] = {"A1"};
String KeyPress;
char* KeyPresses;

void setup() {
  // put your setup code here, to run once:
Serial.begin(115200);
KeyPresses = "A";        // NOTE: KeyPresses used-  not KeyPress
strcat(KeyPresses , KeyPress.c_str());
Serial.println(KeyPresses);

KeyPress = "1";
strcat(KeyPresses , KeyPress.c_str());
Serial.println(KeyPresses);

KeyPress = "2";
strcat(KeyPresses , KeyPress.c_str());
Serial.println(KeyPresses);

KeyPress = "#";
if (KeyPress == "#"){ParseCode(KeyPresses);}

Serial.println("Done");
Serial.println();
}

void ParseCode(char* KeyString){
 int length_KeyPresses = strlen(KeyString);
 Serial.println("KeyPresses = " + String(length_KeyPresses));

  if (strcmp(KeyString, "A1")== 0) {
    //Serial.println("Access Granted"); // commented out, THIS changes serial output
  }else{
    //Serial.println("Access Denied");  // commented out, THIS changes serial output
  }
}
void loop() {
  // put your main code here, to run repeatedly:

}

The serial output works perfectly - until I uncomment the lines in the void ParseCode. In addition, I tried to avoid KeyPress as a String and couldn't.

Well, that was a fun exercise. The short code builds the key presses properly and can discern any length of key codes without having to manually change the size of arrays and changing length of 'for' statements; I couldn't work around the fact I had to use "KeyPress" as String.

const char* A1CODE = {"A13"};
String KeyPress;
char KeyPresses[100] = ""; //setup to allow up to 100 keypress 

void setup() {
  // put your setup code here, to run once:
Serial.begin(115200);
Serial.println("Booting ...");
memset(KeyPresses, 0, sizeof(KeyPresses));
}


void loop() {
  // put your main code here, to run repeatedly:

  SampleKeyPressesFAIL();
  delay(1000);

  SampleKeyPressesPASS();
  delay(1000);
  
 Serial.println(F ("Done"));
 Serial.println();
}

void ParseCode(char* KeyString){

Serial.print("A1CODE = "); Serial.println(A1CODE);
Serial.print("KeyString = "); Serial.println(KeyString); 
  if (String(KeyString) == String(A1CODE)) {
    Serial.println(F ("Access Granted")); 
  }else{
    Serial.println(F ("Access Denied")); 
  }

  memset(KeyPresses, 0, sizeof(KeyPresses));
  Serial.println();
}

void SampleKeyPressesFAIL(){

char keypressed = 'A';
KeyPress = String (keypressed);  //1st key press
strcat(KeyPresses , KeyPress.c_str());

keypressed = '1';
KeyPress = String (keypressed);  //2nd key press
strcat(KeyPresses , KeyPress.c_str());

keypressed = '2';
KeyPress =  String (keypressed)  ;  //3rd key press
strcat(KeyPresses , KeyPress.c_str());


keypressed = '#';
KeyPress =  String (keypressed);  //4th key press and trigger to start comparison
if (KeyPress == "#"){ParseCode(KeyPresses);}

//keypressed = '*';
//KeyPress =  String (keypressed); 
if (KeyPress == "*"){memset(KeyPresses, 0, sizeof(KeyPresses));} // Press "*" to clear string of stored key presses

}

void SampleKeyPressesPASS(){
char keypressed = 'A';
KeyPress = String (keypressed);  //1st key press
strcat(KeyPresses , KeyPress.c_str());

keypressed = '1';
KeyPress = String (keypressed);  //2nd key press
strcat(KeyPresses , KeyPress.c_str());

keypressed = '3';
KeyPress =  String (keypressed)  ;  //3rd key press
strcat(KeyPresses , KeyPress.c_str());


keypressed = '#';
KeyPress =  String (keypressed);  //4th key press and trigger to start comparison
if (KeyPress == "#"){ParseCode(KeyPresses);}

//keypressed = '*';
//KeyPress =  String (keypressed);
if (KeyPress == "*"){memset(KeyPresses, 0, sizeof(KeyPresses));} // Press "*" to clear string of stored key presses
}
char* KeyPresses;

You have a pointer that points nowhere.

KeyPresses = "A";        // NOTE: KeyPresses used-  not KeyPress

Now, you point it at the string literal in ROM.

strcat(KeyPresses , KeyPress.c_str());

And attempt to add more data to the array pointed to, which is sized to hold exactly one character. Fail!

I couldn't work around the fact I had to use "KeyPress" as String.

You could if you recognize that the String class wraps a char ARRAY, NOT a char pointer.

While there are strong similarities between arrays and pointers, those similarities assume that the pointer points to a block of memory that can be read from/written to as needed.

Your attempts to replace a String with the char array that it wraps, without actually allocating a char ARRAY are doomed to failure.

Thank you, PaulS. You're correct in your assessment of my confusion between char arrays and pointers; thus, I'm a little more educated after reading this.