Multi-tap keypad library (or just one function)

I wrote a multi-tap keypad library for anyone to easily input letters and some symbols on a numerical keypad:
Layout here:
This is what it would do to a typical keypad (I have one but didn't hook it up)

This is on my custom pad, which I tested with the help of my wife :). The directional keys and ent esc are actually to the right but my PPT slide is not that wide.

I will upload some videos tomorrow. If you want code, do a reply and don't just lurk :wink:

1 Like

How do you change between letter mode and # mode?

CrossRoads:
How do you change between letter mode and # mode?

In the "standard" keypad library, you call keypad.getKey() to get a key, which is mapped into ASCII characters '0' to '9', and '*', '#' or NO_KEY if no key is pressed. If you just take these return values you get direct mode (face value of '0'-'9' and two more keys). If you instead pass these values to my multi-tap function call like

int multi_tap_result=multi_tap(keypad.getKey());

then the returned multi_tap_result tells you whether a key press has been made via the multi-tap mode (either by long enough wait or by pressing a subsequent key or there is no key press.

Say if you press the '2' key two times and pause, then the straignt key.getKey() will give you some '0' before you press the key, then '2' for your first key press, some 0 between key press, another '2', then all zeros after your second key press. You can use these results to display two '2'. If you instead pass all these numbers('0', '0', ... '2', '0', '0',..., '2', '0', '0', ...) to the multi_tap(), your return would be a bunch of 'A' after your initial key press, then a bunch of 'B' after your second key press, and finally after a long wait you made, a value of 256+'B'. So your receiving end needs to sense if the high byte of the return is 0. If it is, render the returned key in place as the user is still pressing/cycling through the list of characters, if it returns a key with 1 in the high byte, the user has made the choice and you can render this character in place and put it in an input array or something.

Here is my program that interacts with the multi_tap function. It writes on an HD44780 display. The guy outside in the courtyard has been on the ride-on lawn mower since like 2pm and I would have recorded a video if he weren't making so much noise.:

void loop()
{
  int ret=0;
  byte key=panel_keypad.getKey(); // Get a key press from the keypad
  ret=multi_tap(key); // Feed the key press to the multi_tap function.
  if ((ret&256)!=0) // If this is non-zero, we got a key. Handle some special keys or just print the key on screen
  {
    switch (ret&255)
    {
      case '\b':
      if ((pointer%lcd_columns)==0)
      {
        if (rowp>0)
        {
          rowp--;
          pointer=lcd_columns-2;
        }
      }
      else pointer--;
      lcd.setCursor(pointer,rowp);
      lcd.write(' ');
      lcd.setCursor(pointer,rowp);
      return;
      break;
      case '\n':
      pointer=0;
      rowp++;
      rowp%=lcd_rows;
      lcd.setCursor(pointer,rowp);
      return;
      break;
    }
    lcd.setCursor(pointer,rowp);
    lcd.write(lowByte(ret));
    pointer++;
    if (pointer>=lcd_columns)
    {
      pointer=0;
      rowp++;
    }
  }
  else if (ret) // We don't have a key but the user is still cycling through characters on one key so we need to update the screen
  {
    lcd.setCursor(pointer,rowp);
    lcd.write(lowByte(ret));
  }
}

He's probably done now. BTW, the courtyard is big.

Here is a picture. I'll be recording a video soon.

Here we go:

Using the multi-tap library on my phi-panel. This is my latest design. Find out details here:

Quick look around phi-panel:

1 Like

Anyone interested in multi-tap code at all?! I thought there would be more interest.

I find it interesting. Don't have a project in mind yet. Still have your previous board to populate, got a 4x20 plasma screen to play with, got a LCD panel that I've never unpackaged. And now a hot air solder station to check out.

Heh; I did the very same thing last week using a 4x4 telephone-style keypad.

Ok I just posted my code. It's compatible with standard keypads library for arduino and my own version of library.

Hi! I have a problem compiling your code error says "multi_tap_threshold was not declared in this code". What should be the value of multi_tap_threshold and where should I declare it?

here's my code and I'm not sure if this is correct. I'm not good in programming though.

#include <Keypad.h>
#define RESET_MTP '~'

const byte ROWS = 4; //four rows
const byte COLS = 3; //three columns
int pointer;
char keys[ROWS][COLS] = {
  {'1','2','3'},
  {'4','5','6'},
  {'7','8','9'},
  {'*','0','#'}
};
byte rowPins[ROWS] = {11,6,7,9}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {10,12,8}; //connect to the column pinouts of the keypad

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

void setup(){
  Serial.begin(9600);
}
  
void loop()
{
  int mtKey=0;
  byte key=keypad.getKey(); // Get a key press from the keypad
  mtKey=multi_tap(key); // Feed the key press to the multi_tap function.
  Serial.print(mtKey);
}

int multi_tap(byte key)
{
  static boolean upperCase=true;
  static byte prevKeyPress=NO_KEY,cyclicPtr=0;
  static unsigned long prevKeyMillis=0;
  static const char multi_tap_mapping[10][5]={{'0','#','

,'.','?'},{'1','+','-','','/'},{'A','B','C','2','!'},{'D','E','F','3','%'},{'G','H','I','4','('},{'J','K','L','5',')'},{'M','N','O','6','@'},{'P','Q','R','S','7'},{'T','U','V','8',','},{'W','X','Y','Z','9'}};
  if (key==RESET_MTP) // Received reset command. Flush everything and get ready for restart.
  {
    upperCase=true;
    prevKeyPress=NO_KEY;
    cyclicPtr=0;
    return 0;
  }
  if (key!=NO_KEY) // A key is pressed at this iteration.
  {
    if ((key>'9')||(key<'0')) // Function keys
    {
      if ((key==1)||(key=='#')) // Up for case change
      {
        upperCase=!upperCase;
        return 0;
      }
      else // Other function keys. These keys produce characters so they need to terminate the last keypress.
      {
        if (prevKeyPress!=NO_KEY)
        {
          char temp1=multi_tap_mapping[prevKeyPress-'0'][cyclicPtr];
          if ((!upperCase)&&(temp1>='A')&&(temp1<='Z')) temp1+='a'-'A';
          cyclicPtr=0;
          prevKeyMillis=0;
          switch (key)
          {
            case 2:
            // Call symbol list
            return 0; // Clear the buffer.
            break;
            case 3:
            prevKeyPress='\b';
            break;
            case 4:
            case '
':
            prevKeyPress=' ';
            break;
            case 5:
            prevKeyPress='\n';
            break;
            case 6:
            prevKeyPress=NO_KEY; // Clear the buffer.
            break;
          }
          return(256+(unsigned int)(temp1));
        }
        else
        {
          prevKeyPress=NO_KEY;
          cyclicPtr=0;
          prevKeyMillis=0;
          switch (key)
          {
            case 2:
            // Call symbol list
            return 0; // Clear the buffer.
            break;
            case 3:
            return (256+(unsigned int)('\b'));
            break;
            case 4:
            return (256+(unsigned int)(' '));
            break;
            case 5:
            return (256+(unsigned int)('\n'));
            break;
            case 6:
            return 0; // Clear the buffer.
            break;
          }
        }
      }
   
    }
    if (prevKeyPress!=NO_KEY)
    {
      if (prevKeyPress==key)
      {
        char temp1;
        cyclicPtr++;
        if ((multi_tap_mapping[key-'0'][cyclicPtr]==0)||(cyclicPtr==5)) cyclicPtr=0; //Cycle key
        prevKeyMillis=millis();
        temp1=multi_tap_mapping[key-'0'][cyclicPtr];
        if ((!upperCase)&&(temp1>='A')&&(temp1<='Z')) temp1+='a'-'A';
        return ((unsigned int)(temp1));
      }
      else
      {
        char temp1=multi_tap_mapping[prevKeyPress-'0'][cyclicPtr];
        if ((!upperCase)&&(temp1>='A')&&(temp1<='Z')) temp1+='a'-'A';
        prevKeyPress=key;
        cyclicPtr=0;
        prevKeyMillis=millis();
        //Print key on cursor+1
        return(256+(unsigned int)(temp1));
      }
    }
    else
    {
      char temp1=multi_tap_mapping[key-'0'][cyclicPtr];
      if ((!upperCase)&&(temp1>='A')&&(temp1<='Z')) temp1+='a'-'A';
      prevKeyPress=key;
      prevKeyMillis=millis();
      cyclicPtr=0;
      return ((unsigned int)(temp1));
    }
 
  }
  else // No key is pressed at this iteration.
  {
    if (prevKeyPress==NO_KEY) return 0; // No key was previously pressed.
    else if (millis()-prevKeyMillis<multi_tap_threshold) // Key was pressed previously but within threshold
    {
      char temp1=multi_tap_mapping[prevKeyPress-'0'][cyclicPtr];
      if ((!upperCase)&&(temp1>='A')&&(temp1<='Z')) temp1+='a'-'A';
      return((unsigned int)(temp1));
    }
    else // Key was pressed previously and threshold has passed
    {
      char temp1=multi_tap_mapping[prevKeyPress-'0'][cyclicPtr];
      if ((!upperCase)&&(temp1>='A')&&(temp1<='Z')) temp1+='a'-'A';
      prevKeyPress=NO_KEY;
      cyclicPtr=0;
      return(256+(unsigned int)(temp1));
    }
  }
  return 0;
}

Hi! I have a problem compiling your code error says "multi_tap_threshold was not declared in this code". What should be the value of multi_tap_threshold and where should I declare it?

here's my code and I'm not sure if this is correct. I'm not good in programming though.

#include <Keypad.h>
#define RESET_MTP '~'

const byte ROWS = 4; //four rows
const byte COLS = 3; //three columns
int pointer;
char keys[ROWS][COLS] = {
  {'1','2','3'},
  {'4','5','6'},
  {'7','8','9'},
  {'*','0','#'}
};
byte rowPins[ROWS] = {11,6,7,9}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {10,12,8}; //connect to the column pinouts of the keypad

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

void setup(){
  Serial.begin(9600);
}
  
void loop()
{
  int mtKey=0;
  byte key=keypad.getKey(); // Get a key press from the keypad
  mtKey=multi_tap(key); // Feed the key press to the multi_tap function.
  Serial.print(mtKey);
}

int multi_tap(byte key)
{
  static boolean upperCase=true;
  static byte prevKeyPress=NO_KEY,cyclicPtr=0;
  static unsigned long prevKeyMillis=0;
  static const char multi_tap_mapping[10][5]={{'0','#','

,'.','?'},{'1','+','-','','/'},{'A','B','C','2','!'},{'D','E','F','3','%'},{'G','H','I','4','('},{'J','K','L','5',')'},{'M','N','O','6','@'},{'P','Q','R','S','7'},{'T','U','V','8',','},{'W','X','Y','Z','9'}};
  if (key==RESET_MTP) // Received reset command. Flush everything and get ready for restart.
  {
    upperCase=true;
    prevKeyPress=NO_KEY;
    cyclicPtr=0;
    return 0;
  }
  if (key!=NO_KEY) // A key is pressed at this iteration.
  {
    if ((key>'9')||(key<'0')) // Function keys
    {
      if ((key==1)||(key=='#')) // Up for case change
      {
        upperCase=!upperCase;
        return 0;
      }
      else // Other function keys. These keys produce characters so they need to terminate the last keypress.
      {
        if (prevKeyPress!=NO_KEY)
        {
          char temp1=multi_tap_mapping[prevKeyPress-'0'][cyclicPtr];
          if ((!upperCase)&&(temp1>='A')&&(temp1<='Z')) temp1+='a'-'A';
          cyclicPtr=0;
          prevKeyMillis=0;
          switch (key)
          {
            case 2:
            // Call symbol list
            return 0; // Clear the buffer.
            break;
            case 3:
            prevKeyPress='\b';
            break;
            case 4:
            case '
':
            prevKeyPress=' ';
            break;
            case 5:
            prevKeyPress='\n';
            break;
            case 6:
            prevKeyPress=NO_KEY; // Clear the buffer.
            break;
          }
          return(256+(unsigned int)(temp1));
        }
        else
        {
          prevKeyPress=NO_KEY;
          cyclicPtr=0;
          prevKeyMillis=0;
          switch (key)
          {
            case 2:
            // Call symbol list
            return 0; // Clear the buffer.
            break;
            case 3:
            return (256+(unsigned int)('\b'));
            break;
            case 4:
            return (256+(unsigned int)(' '));
            break;
            case 5:
            return (256+(unsigned int)('\n'));
            break;
            case 6:
            return 0; // Clear the buffer.
            break;
          }
        }
      }
   
    }
    if (prevKeyPress!=NO_KEY)
    {
      if (prevKeyPress==key)
      {
        char temp1;
        cyclicPtr++;
        if ((multi_tap_mapping[key-'0'][cyclicPtr]==0)||(cyclicPtr==5)) cyclicPtr=0; //Cycle key
        prevKeyMillis=millis();
        temp1=multi_tap_mapping[key-'0'][cyclicPtr];
        if ((!upperCase)&&(temp1>='A')&&(temp1<='Z')) temp1+='a'-'A';
        return ((unsigned int)(temp1));
      }
      else
      {
        char temp1=multi_tap_mapping[prevKeyPress-'0'][cyclicPtr];
        if ((!upperCase)&&(temp1>='A')&&(temp1<='Z')) temp1+='a'-'A';
        prevKeyPress=key;
        cyclicPtr=0;
        prevKeyMillis=millis();
        //Print key on cursor+1
        return(256+(unsigned int)(temp1));
      }
    }
    else
    {
      char temp1=multi_tap_mapping[key-'0'][cyclicPtr];
      if ((!upperCase)&&(temp1>='A')&&(temp1<='Z')) temp1+='a'-'A';
      prevKeyPress=key;
      prevKeyMillis=millis();
      cyclicPtr=0;
      return ((unsigned int)(temp1));
    }
 
  }
  else // No key is pressed at this iteration.
  {
    if (prevKeyPress==NO_KEY) return 0; // No key was previously pressed.
    else if (millis()-prevKeyMillis<multi_tap_threshold) // Key was pressed previously but within threshold
    {
      char temp1=multi_tap_mapping[prevKeyPress-'0'][cyclicPtr];
      if ((!upperCase)&&(temp1>='A')&&(temp1<='Z')) temp1+='a'-'A';
      return((unsigned int)(temp1));
    }
    else // Key was pressed previously and threshold has passed
    {
      char temp1=multi_tap_mapping[prevKeyPress-'0'][cyclicPtr];
      if ((!upperCase)&&(temp1>='A')&&(temp1<='Z')) temp1+='a'-'A';
      prevKeyPress=NO_KEY;
      cyclicPtr=0;
      return(256+(unsigned int)(temp1));
    }
  }
  return 0;
}

brutal_magnet:
Hi! I have a problem compiling your code error says "multi_tap_threshold was not declared in this code". What should be the value of multi_tap_threshold and where should I declare it?

here's my code and I'm not sure if this is correct. I'm not good in programming though.

Just do #define multi_tap_threshold 1000

This is the time the function waits until a letter is confirmed. Say you press "2" three times, and wait for 1000 ms then the letter C becomes the output. If you want a longer wait, you can increase the 1000.

Updated code:

I made changes so that you only see the real output, the C, not the temporary such as A then B then C.

The multi-tap only works well on a display, not a serial port, although you can do your preliminary test over a serial port, you can't render a character in place.

#include <Keypad.h>

#define multi_tap_threshold 1000
#define RESET_MTP '~'

const byte ROWS = 4; //four rows
const byte COLS = 3; //three columns
int pointer;
char keys[ROWS][COLS] = {
  {'1','2','3'},
  {'4','5','6'},
  {'7','8','9'},
  {'*','0','#'}
};
byte rowPins[ROWS] = {11,6,7,9}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {10,12,8}; //connect to the column pinouts of the keypad

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

void setup(){
  Serial.begin(9600);
}
 
void loop()
{
  int mtKey=0;
  byte key=keypad.getKey(); // Get a key press from the keypad
  mtKey=multi_tap(key); // Feed the key press to the multi_tap function.
  if(!(mtKey&10000000B)) Serial.print(mtKey);
}

int multi_tap(byte key)
{
  static boolean upperCase=true;
  static byte prevKeyPress=NO_KEY,cyclicPtr=0;
  static unsigned long prevKeyMillis=0;
  static const char multi_tap_mapping[10][5]={{'0','#','

,'.','?'},{'1','+','-','','/'},{'A','B','C','2','!'},{'D','E','F','3','%'},{'G','H','I','4','('},{'J','K','L','5',')'},{'M','N','O','6','@'},{'P','Q','R','S','7'},{'T','U','V','8',','},{'W','X','Y','Z','9'}};
  if (key==RESET_MTP) // Received reset command. Flush everything and get ready for restart.
  {
    upperCase=true;
    prevKeyPress=NO_KEY;
    cyclicPtr=0;
    return 0;
  }
  if (key!=NO_KEY) // A key is pressed at this iteration.
  {
    if ((key>'9')||(key<'0')) // Function keys
    {
      if ((key==1)||(key=='#')) // Up for case change
      {
        upperCase=!upperCase;
        return 0;
      }
      else // Other function keys. These keys produce characters so they need to terminate the last keypress.
      {
        if (prevKeyPress!=NO_KEY)
        {
          char temp1=multi_tap_mapping[prevKeyPress-'0'][cyclicPtr];
          if ((!upperCase)&&(temp1>='A')&&(temp1<='Z')) temp1+='a'-'A';
          cyclicPtr=0;
          prevKeyMillis=0;
          switch (key)
          {
            case 2:
            // Call symbol list
            return 0; // Clear the buffer.
            break;
            case 3:
            prevKeyPress='\b';
            break;
            case 4:
            case '
':
            prevKeyPress=' ';
            break;
            case 5:
            prevKeyPress='\n';
            break;
            case 6:
            prevKeyPress=NO_KEY; // Clear the buffer.
            break;
          }
          return(256+(unsigned int)(temp1));
        }
        else
        {
          prevKeyPress=NO_KEY;
          cyclicPtr=0;
          prevKeyMillis=0;
          switch (key)
          {
            case 2:
            // Call symbol list
            return 0; // Clear the buffer.
            break;
            case 3:
            return (256+(unsigned int)('\b'));
            break;
            case 4:
            return (256+(unsigned int)(' '));
            break;
            case 5:
            return (256+(unsigned int)('\n'));
            break;
            case 6:
            return 0; // Clear the buffer.
            break;
          }
        }
      }
   
    }
    if (prevKeyPress!=NO_KEY)
    {
      if (prevKeyPress==key)
      {
        char temp1;
        cyclicPtr++;
        if ((multi_tap_mapping[key-'0'][cyclicPtr]==0)||(cyclicPtr==5)) cyclicPtr=0; //Cycle key
        prevKeyMillis=millis();
        temp1=multi_tap_mapping[key-'0'][cyclicPtr];
        if ((!upperCase)&&(temp1>='A')&&(temp1<='Z')) temp1+='a'-'A';
        return ((unsigned int)(temp1));
      }
      else
      {
        char temp1=multi_tap_mapping[prevKeyPress-'0'][cyclicPtr];
        if ((!upperCase)&&(temp1>='A')&&(temp1<='Z')) temp1+='a'-'A';
        prevKeyPress=key;
        cyclicPtr=0;
        prevKeyMillis=millis();
        //Print key on cursor+1
        return(256+(unsigned int)(temp1));
      }
    }
    else
    {
      char temp1=multi_tap_mapping[key-'0'][cyclicPtr];
      if ((!upperCase)&&(temp1>='A')&&(temp1<='Z')) temp1+='a'-'A';
      prevKeyPress=key;
      prevKeyMillis=millis();
      cyclicPtr=0;
      return ((unsigned int)(temp1));
    }
 
  }
  else // No key is pressed at this iteration.
  {
    if (prevKeyPress==NO_KEY) return 0; // No key was previously pressed.
    else if (millis()-prevKeyMillis<multi_tap_threshold) // Key was pressed previously but within threshold
    {
      char temp1=multi_tap_mapping[prevKeyPress-'0'][cyclicPtr];
      if ((!upperCase)&&(temp1>='A')&&(temp1<='Z')) temp1+='a'-'A';
      return((unsigned int)(temp1));
    }
    else // Key was pressed previously and threshold has passed
    {
      char temp1=multi_tap_mapping[prevKeyPress-'0'][cyclicPtr];
      if ((!upperCase)&&(temp1>='A')&&(temp1<='Z')) temp1+='a'-'A';
      prevKeyPress=NO_KEY;
      cyclicPtr=0;
      return(256+(unsigned int)(temp1));
    }
  }
  return 0;
}

Just do #define multi_tap_threshold 1000

This is the time the function waits until a letter is confirmed. Say you press "2" three times, and wait for 1000 ms then the letter C becomes the output. If you want a longer wait, you can increase the 1000.

Hi! Well as for the code that you updated, it doesn't compile "error: invalid suffix 'B' on integer constant". I tried removing the 'B' on the if (!mtkey&10000000B) which becomes if (!mtkey&10000000), it compiles but no output..

And may I ask, how can I store the input from the keypad? By the way I'm using 4x3 keypad. Say I entered, '1' then 'a' then 'b', those letters will be saved and stored to char output[4] so if I do lcd.print(output), it will display '1ab'. Thank you!

Typo. Should be B10000000.

You should read these lines at the beginning of my code before you started, especially step 3:

Also, remapping your 'A' key into '#' key will make it into upper/lower case function.

This portion of code is the multi-tap function written by Dr. John Liu. It is provided to you without any warranty. Your use is limited to not-for-profit activities.
You may change the definition of the multi-tap keypad, which contains 5 symbols per numerical key for all 10 numerical keys.
To use the function:

  1. Call your keypad's getKey method to receive a key. This key has to be '0' to '9' or other symbols such as '#' or NO_KEY or scan codes for phi_keypads objects.
  2. Call multi_tap and pass the received key to it.
  3. The returned value indicates what is going on.
    If the most significant bit (MSB) is set to 1, then the multi-tap is in process. You should print the return value (with MSB reset to 0) in place.
    If MSB is reset to 0, then the multi-tap has concluded and a final character is produced. You should print this character in place and move cursor to the next position.
    If the return value is NO-KEY, then nothing is happening.
    Pressing the '#' key engages or disengages shift to switch between upper and lower cases.
    Calling the multi_tap with RESET_MTP will result in the multi_tap process to reset, useful if you are unsure of the function's status. Say you didn't finish a multi-tap process and switch to another piece of code and then later want to use multi-tap, you should reset it before using. The return value for this call is to be discarded.

HI liudr,

your layout of the multitap resembles the layout ot letters on mobile phones which is/was be patented. might be a point of attention if it still is (or some variation)

There are some differences though on your custom pad e.g. the 2x5 layout.

Thanks Rob. I will look into it. Here is something I found:

http://www.itu.int/rec/dologin_pub.asp?lang=e&id=T-REC-E.161-200102-I!!PDF-E&type=items

There is no patent on it.

hi! can you help me with my 4x3 keypad matrix? I want it to do the same thing as your phi-panel but I just don;t know how..TIA!

When you tried this code, what did you see? You got to give me something (symptoms) to get help:

Plus, as I said, using multi-tap on serial monitor is not good since serial monitor can't render a character in place. You should use serial monitor for trying it out and use an LCD for serious results.

#include <Keypad.h>
#define multi_tap_threshold 1000
#define RESET_MTP '~'

const byte ROWS = 4; //four rows
const byte COLS = 3; //three columns
int pointer;
char keys[ROWS][COLS] = {
  {'1','2','3'},
  {'4','5','6'},
  {'7','8','9'},
  {'*','0','#'}
};
byte rowPins[ROWS] = {11,6,7,9}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {10,12,8}; //connect to the column pinouts of the keypad

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

void setup(){
  Serial.begin(9600);
}
  
void loop()
{
  int mtKey=0;
  byte key=keypad.getKey(); // Get a key press from the keypad
  mtKey=multi_tap(key); // Feed the key press to the multi_tap function.
  if(!(mtKey&B10000000)) Serial.print(mtKey);
}

int multi_tap(byte key)
{
  static boolean upperCase=true;
  static byte prevKeyPress=NO_KEY,cyclicPtr=0;
  static unsigned long prevKeyMillis=0;
  static const char multi_tap_mapping[10][5]={{'0','#','

,'.','?'},{'1','+','-','','/'},{'A','B','C','2','!'},{'D','E','F','3','%'},{'G','H','I','4','('},{'J','K','L','5',')'},{'M','N','O','6','@'},{'P','Q','R','S','7'},{'T','U','V','8',','},{'W','X','Y','Z','9'}};
  if (key==RESET_MTP) // Received reset command. Flush everything and get ready for restart.
  {
    upperCase=true;
    prevKeyPress=NO_KEY;
    cyclicPtr=0;
    return 0;
  }
  if (key!=NO_KEY) // A key is pressed at this iteration.
  {
    if ((key>'9')||(key<'0')) // Function keys
    {
      if ((key==1)||(key=='#')) // Up for case change
      {
        upperCase=!upperCase;
        return 0;
      }
      else // Other function keys. These keys produce characters so they need to terminate the last keypress.
      {
        if (prevKeyPress!=NO_KEY)
        {
          char temp1=multi_tap_mapping[prevKeyPress-'0'][cyclicPtr];
          if ((!upperCase)&&(temp1>='A')&&(temp1<='Z')) temp1+='a'-'A';
          cyclicPtr=0;
          prevKeyMillis=0;
          switch (key)
          {
            case 2:
            // Call symbol list
            return 0; // Clear the buffer.
            break;
            case 3:
            prevKeyPress='\b';
            break;
            case 4:
            case '
':
            prevKeyPress=' ';
            break;
            case 5:
            prevKeyPress='\n';
            break;
            case 6:
            prevKeyPress=NO_KEY; // Clear the buffer.
            break;
          }
          return(256+(unsigned int)(temp1));
        }
        else
        {
          prevKeyPress=NO_KEY;
          cyclicPtr=0;
          prevKeyMillis=0;
          switch (key)
          {
            case 2:
            // Call symbol list
            return 0; // Clear the buffer.
            break;
            case 3:
            return (256+(unsigned int)('\b'));
            break;
            case 4:
            return (256+(unsigned int)(' '));
            break;
            case 5:
            return (256+(unsigned int)('\n'));
            break;
            case 6:
            return 0; // Clear the buffer.
            break;
          }
        }
      }
   
    }
    if (prevKeyPress!=NO_KEY)
    {
      if (prevKeyPress==key)
      {
        char temp1;
        cyclicPtr++;
        if ((multi_tap_mapping[key-'0'][cyclicPtr]==0)||(cyclicPtr==5)) cyclicPtr=0; //Cycle key
        prevKeyMillis=millis();
        temp1=multi_tap_mapping[key-'0'][cyclicPtr];
        if ((!upperCase)&&(temp1>='A')&&(temp1<='Z')) temp1+='a'-'A';
        return ((unsigned int)(temp1));
      }
      else
      {
        char temp1=multi_tap_mapping[prevKeyPress-'0'][cyclicPtr];
        if ((!upperCase)&&(temp1>='A')&&(temp1<='Z')) temp1+='a'-'A';
        prevKeyPress=key;
        cyclicPtr=0;
        prevKeyMillis=millis();
        //Print key on cursor+1
        return(256+(unsigned int)(temp1));
      }
    }
    else
    {
      char temp1=multi_tap_mapping[key-'0'][cyclicPtr];
      if ((!upperCase)&&(temp1>='A')&&(temp1<='Z')) temp1+='a'-'A';
      prevKeyPress=key;
      prevKeyMillis=millis();
      cyclicPtr=0;
      return ((unsigned int)(temp1));
    }
 
  }
  else // No key is pressed at this iteration.
  {
    if (prevKeyPress==NO_KEY) return 0; // No key was previously pressed.
    else if (millis()-prevKeyMillis<multi_tap_threshold) // Key was pressed previously but within threshold
    {
      char temp1=multi_tap_mapping[prevKeyPress-'0'][cyclicPtr];
      if ((!upperCase)&&(temp1>='A')&&(temp1<='Z')) temp1+='a'-'A';
      return((unsigned int)(temp1));
    }
    else // Key was pressed previously and threshold has passed
    {
      char temp1=multi_tap_mapping[prevKeyPress-'0'][cyclicPtr];
      if ((!upperCase)&&(temp1>='A')&&(temp1<='Z')) temp1+='a'-'A';
      prevKeyPress=NO_KEY;
      cyclicPtr=0;
      return(256+(unsigned int)(temp1));
    }
  }
  return 0;
}

When you tried this code, what did you see? You got to give me something (symptoms) to get help:

Whenever I tried to open the Serial Monitor I only get never ending 0's. And also I'm thinking about displaying it on LCD too so maybe I should only replace Serial.begin(9600) with lcd.begin(16,2) and Serial.print(mtKey) with lcd.print(mtKey)??

Since you can re-locate your LCD cursor, you want to think about that, lcd.serCursor(0,0); lcd.print()

Try this mod:

if(!(mtKey&B10000000)) Serial.print(mtKey);

into

if((!(mtKey&B10000000))&&(mtKey!=0)) Serial.print((char)mtKey);

I will check the code when I have some time tonight.