Multi-Purpose IO Card

When we are working on a prototype, we always need access to pushbuttons, encoders and even displays to test our ideas in the real world. This Multi-Purpose IO Card was designed to help me do just that…

What is on the PCB?

This PCB was designed with my particular work style in mind. I use a lot of I2C devices, IO Expanders, Displays and sensors. It would thus make sense to have I2C on the PCB, to control an OLED display, as well as a PCF8574 IO expander, that is used to drive a 4×4 Matrix Keypad. Two Rotary encoders, as well as another 4 standard push buttons completes the PCB…

The features, summarised is as follows:

1x Matrix Keypad (4×4) Controlled via an PCF8574 IO expander with selectable addressing.
1x SSD1306 OLED I2C Display
4x Momentary pushbuttons, configured to be used with internal pullups – i.e pushing the button pulls the GPIO LOW
2x Rotary Encoders, with integrated Pushbutton, also configured as Active LOW

The board has all of the connectors and jumpers on the back, making it possible to mount it to an enclosure as a control panel.

I have also provided an additional I2C header to make it possible to add additional devices to the I2C bus easily

The PCB in Detail

PCB Top

Starting from left to right, we have two push-buttons, an OLED display, with two rotary encoders below the display, and another two momentary push buttons. On the Right, we have a 4×4 matrix keypad, and various pin headers for connection to a microcontroller of your choice.

On the back, we have the PCF8574 IO expander for the Matric keypad, addressing Jumpers for the IO expander, as well as the two pin headers for connections to and from a microcontroller…

The Pinouts of these are as follows:
Horizontal 15 pin 2.54mm connector
SDA I2C Data
SCA I2C Clock

GND

SW4 Momentary Push Button 4
SW3 Momentary Push Button 3
SW2 Momentary Push Button 2
SW1 Momentary Push Button 1

RE2-D Rotary Encoder 2 Push Button
RE2-B Rotary Encoder 2 Pin B
RE2-A Rotary Encoder 2 Pin A

RE1-D Rotary Encoder 1 Push Button
RE1-B Rotary Encoder 1 Pin B
RE1-A Rotary Encoder 1 Pin A

GND
VCC 3.3v to 5v DC

The Expansion header extends the I2C Bus, as well as proved access to the interrupt pin on the PCF8574. VCC and GND are also provided.

The Schematic


Manufacturing the PCB

I choose PCBWay for my PCB manufacturing. Why? What makes them different from the rest?

PCBWay‘s business goal is to be the most professional PCB manufacturer for prototyping and low-volume production work in the world. With more than a decade in the business, they are committed to meeting the needs of their customers from different industries in terms of quality, delivery, cost-effectiveness and any other demanding requests. As one of the most experienced PCB manufacturers and SMT Assemblers in China, they pride themselves to be our (the Makers) best business partners, as well as good friends in every aspect of our PCB manufacturing needs. They strive to make our R&D work easy and hassle-free.

How do they do that?

PCBWay is NOT a broker. That means that they do all manufacturing and assembly themselves, cutting out all the middlemen, and saving us money.

PCBWay’s online quoting system gives a very detailed and accurate picture of all costs upfront, including components and assembly costs. This saves a lot of time and hassle.

PCBWay gives you one-on-one customer support, that answers you in 5 minutes ( from the Website chat ), or by email within a few hours ( from your personal account manager). Issues are really resolved very quickly, not that there are many anyway, but, as we are all human, it is nice to know that when a gremlin rears its head, you have someone to talk to that will do his/her best to resolve your issue as soon as possible.

Find out more here

Assembly and Testing

The assembly of this PCB was relatively easy, as it contains only a single SMD component. I do however have to alert you to a certain caveat…

On the PCB, the I2C OLED display pinout is, from left to right,

VCC GND SDC SDA

I have however come across similar displays that swap the GND and VCC pins… and some that even have SCL and SDA swapped…

It is thus quite important that you check your display BEFORE soldering it to this PCB…

Addressing the PCF8574 is also quite easy, with the jumper towards the top is a high, and towards the bottom is a low… They are marked A2 A1 A0 and thus, counting in binary, all low will be 0x20h and all high will be ox27h

Also, note that there are NO I2C Pullup resistors on the board. My microcontroller PCB’s usually have these already, and most I2C Sensors, including the OLED Display that we use, already include as well…
You should thus check what you have on your own hardware, as it is quite impossible to cater for every situation… In a future version, I may add selectable pullup resistors onto this board as well…

Coding and Firmware

The possible uses of this board is quite broad, and the code possibilities are thus also quite extensive. Since I mainly use ESPHome or the Arduino IDE with most of my projects, I wont be including any specialised code here. I think it is enough to say that almost all of the available PCF8574 Matrix Keypad libraries available for the Arduino IDE will fork with this board…

The pinouts are important, and thus :

Row 0 – P0
Row 1 – P1
Row 2 – P2
Row 4 – P3

Col 0 – P7
Col 1 – P6
Col 2 – P5
Col 3 – P4

As far as ESPHome goes, you will need to
1) Add an I2c bus for your device
2)Add a PCF8574 component
3)Add a Matrix Keypad component, and refer the rows and columns to the pins on the PCF8574 – See below for an example of how I have done that in a previous project.

#I2C bus

i2c:

sda: 4

scl: 5

scan: true

id: I2C_Bus

#
# In my case, SDA is on GPIO4 and SCL is on GPIO5
# This is similar to the standard configuration on a NodeMCU v2 Dev board
#

#
# The next step is to configure the actual IO Expander, which in my case is located 
# at address 0x27
#

#PCF8574

pcf8574:

- id: 'pcf8574_hub'

address: 0x27

pcf8575: false


#
# Now we can add the actual keypad interface to the YAML file
# Take note of the difference from the ESP32 file above.
#
#

#KEYPAD

matrix_keypad:

id: mykeypad

rows:

- pin:

pcf8574: pcf8574_hub

# Use pin number 0

number: 0

# In the ESP32 file, we wHereould specify a pin directly like:
#
# -pin: 17
#
# That approach will not work for us.
# The reason for that is that we have to redirect the GPIO to a 
# physical pin on the PCF8574 IO expander.
#
# That is done with the following syntax
#
# - pin:
#pcf8574: pcf8574_hub -- This is the ID of the PCF8574 device -
#number: 0 -- The actual pin number

- pin:

pcf8574: pcf8574_hub

# Use pin number 0

number: 1

- pin:

pcf8574: pcf8574_hub

# Use pin number 0

number: 2

- pin:

pcf8574: pcf8574_hub

# Use pin number 0

number: 3

columns:

- pin:

pcf8574: pcf8574_hub

# Use pin number 0

number: 7

- pin:

pcf8574: pcf8574_hub

# Use pin number 0

number: 6

- pin:

pcf8574: pcf8574_hub

# Use pin number 0

number: 5

- pin:

pcf8574: pcf8574_hub

# Use pin number 0

number: 4

keys: "123A456B789C*0#D"

has_diodes: false

The Rotary encoders and momentary push-buttons can be handled in the same manner, using standard libraries in the Arduino IDE, or a rotary encoder component in ESPHome…

The OLED display would also be handled as above, with a DISPLAY component in ESPHome…

Summary and next steps

The next steps, for me at least, would be to design and CNC cut a suitable enclosure for the IO panel/Control panel in order to make it easier to use…

The panel was designed to be a tool to aid me while designing, and part of my never-ending battle getting rid of breadboards.

It does its job well, at least so far, and works as I have intended it to.

An I2C Matrix Keypad

The completed I2C Matrix Keypad

In a previous post this month I introduced my 4×4 matrix keypad. That keypad was designed to be directly interfaced to a microcontroller’s GPIO pins or alternatively to an IO expander chip like the PCF8574. That design, while working very well had the problem of requiring 8 GPIO pins to function correctly.

GPIO pins on a microcontroller can be considered very precious resources, and it should then be logical to assume that we should find a way to use these GPIO pins in a more conservative way, to allow us to interface more peripherals.

I solved this problem by integrating the keypad with an IO Expander on the same PCB. That will allow us to get away with using only 2 GPIO pins, and also open up the option of adding more keypads to the I2C bus, in the event that we need that many keys for a particular project.

The Schematic

I2C 4×4 Matrix Keypad Schematic

Looking closely at the schematic, we can see that it is exactly the same basic keypad circuit that I used in the initial design. The only difference is that in this design, I have integrated a PCF8574 directly onto the PCB.

Some additional features include selectable I2C Pullup resistors ( usually my microcontroller development boards already include those) that can be activated with a jumper when needed. There are also a set of address selection jumpers, making it possible to stack keypads together into a bigger keyboard if you require something like that. Note that, in this version of the hardware, I did not include headers for stacking.

The keypad can be powered by a DC power source of 3.3v to 5v.

The PCB

I2C Keypad PCB
3D Render of the I2C Keypad

The PCB is a double-layer board of 68.8mm x 50.8mm. Male header pins provide access to the connections as well as address and pullup resistor jumpers. In my build, I have mounted these male headers on the back of the PCB. That makes it possible to mount the Keypad in an enclosure without having the jumpers “stick out” and get in the way.

The top layer of the I2C Keypad PCB
Bottom Layer

Manufacturing

I choose PCBWay for my PCB manufacturing.
This month, PCBWay is also celebrating its 9th anniversary, and that means that there are quite a lot of very special offers available.

Why?
What makes them different from the rest?

PCBWay‘s business goal is to be the most professional PCB manufacturer for prototyping and low-volume production work in the world. With more than a decade in the business, they are committed to meeting the needs of their customers from different industries in terms of quality, delivery, cost-effectiveness and any other demanding requests. As one of the most experienced PCB manufacturers and SMT Assemblers in China, they pride themselves to be our (the Makers) best business partners, as well as good friends in every aspect of our PCB manufacturing needs. They strive to make our R&D work easy and hassle-free.

How do they do that?

PCBWay is NOT a broker. That means that they do all manufacturing and assembly themselves, cutting out all the middlemen, and saving us money.

PCBWay’s online quoting system gives a very detailed and accurate picture of all costs upfront, including components and assembly costs. This saves a lot of time and hassle.

PCBWay gives you one-on-one customer support, that answers you in 5 minutes ( from the Website chat ), or by email within a few hours ( from your personal account manager). Issues are really resolved very quickly, not that there are many anyway, but, as we are all human, it is nice to know that when a gremlin rears its head, you have someone to talk to that will do his/her best to resolve your issue as soon as possible.

Find out more here

Assembly

The assembly of this PCB was quite easy and quick. A stencil is not required. All SMD components are 0805 or bigger. It would thus be quite easy to solder them all by hand with a fine-tipped soldering iron.

I have however used soldering paste and hot air to reflow the components, as it is the fastest, in my opinion, and definitely looks neater than hand soldering.

After placing SMD components onto solder paste – ready for reflow soldering
After Reflow soldering with Hot Air

The board is now ready to solder the switches and header pins in place. As already mentioned above, I chose to assemble the headers on the back of the PCB to prevent them from interfering with any enclosure that I may later use with the keypad.

Final Assembly
Note that I assembled the headers onto the back of the PCB.

Testing and Coding

Testing the keypad consisted of a few steps, the first of which was ensuring that there were no short circuits, as well as that all the momentary switches worked.
This was done with a multimeter in continuity as well as diode mode, with probes alternatively on each column and row in turn, while pressing the buttons.

The next stage was testing the I2C IO Expander. This was done with a simple I2C Scanning sketch on an Arduino Uno. It did not do a lot, but, I could see that the PCF8574 is responding to its address and that the pullup resistors work when enabled. This test was repeated with my own ESP8266 and ESP32 boards, this time with pullup resistors disabled, as these boards already have them onboard.

Coding came next, and it was another case of perspectives. It seems like all commercial keypads do not have diodes. This affects the way that they work with a given library. It seems that software developers and hardware developers have different understandings of what a row and a column is.

This meant that, due to the fact that I have diodes on each switch, and the way that the library work – which pins are pulled high and which are set as inputs -, I had to swap around my rows and columns in the software to get everything to work. On a keypad with the diodes replaced with 0-ohm links, that was not needed.

A short test sketch follows below:

Note that with was run on an ESP8266-12E, therefore the Wire.begin() function was changed to Wire.begin(4,5); in order to use GPIO 4 and GPIO 5 for I2C

Another point to note is that the keypad Layout will seem strange. Remember that this is due to the diodes in series on each switch. That forces us to swap around the Rows and the Columns in the software, resulting in a mirrored and rotated left representation of the keypad. It looks funny, but believe me, it actually still works perfectly.

#include <Wire.h>
#include "Keypad.h"
#include <Keypad_I2C.h>

const byte n_rows = 4;
const byte n_cols = 4;

char keys[n_rows][n_cols] = {
    {'1', '4', '7', '*'},
    {'2', '5', '8', '0'},
    {'3', '6', '9', '#'},
    {'A', 'B', 'C', 'D'}};

byte rowPins[n_rows] = {4, 5, 6, 7};
byte colPins[n_cols] = {0, 1, 2, 3};

Keypad_I2C myKeypad = Keypad_I2C(makeKeymap(keys), rowPins, colPins, n_rows, n_cols, 0x20);

String swOnState(KeyState kpadState)
{
    switch (kpadState)
    {
    case IDLE:
        return "IDLE";
        break;
    case PRESSED:
        return "PRESSED";
        break;
    case HOLD:
        return "HOLD";
        break;
    case RELEASED:
        return "RELEASED";
        break;
    } // end switch-case
    return "";
} // end switch on state function

void setup()
{
    // This will be called by App.setup()
    Serial.begin(115200);
    while (!Serial)
    { /*wait*/
    }
    Serial.println("Press any key...");
    Wire.begin(4,5);
    myKeypad.begin(makeKeymap(keys));
}

char myKeyp = NO_KEY;
KeyState myKSp = IDLE;
auto myHold = false;

void loop()
{

    char myKey = myKeypad.getKey();
    KeyState myKS = myKeypad.getState();

    if (myKSp != myKS && myKS != IDLE)
    {
        Serial.print("myKS: ");
        Serial.println(swOnState(myKS));
        myKSp = myKS;
        if (myKey != NULL)
            myKeyp = myKey;
        String r;
        r = myKeyp;
        Serial.println("myKey: " + String(r));
        if (myKS == HOLD)
            myHold = true;
        if (myKS == RELEASED)
        {
            if (myHold)
                r = r + "+";
            Serial.println(r.c_str());
            myHold = false;
        }
        Serial.println(swOnState(myKS));
        myKey == NULL;
        myKS = IDLE;
    }
}

Conclusion

This project once again delivered what I set out to achieve. It has some quirks, but nothing serious. Everything works as expected, both in the Arduino IDE/platform IO realm, as well as in ESPHome. It is worth noting that in ESPHome, we do not need to swap the rows and columns to use the Keypad component. Do remember to leave the has_diodes flag to false though…

A Reliable Matrix Keypad

What is a matrix keypad?

A matrix keypad is a type of keypad that uses a matrix of wires to connect the keys to the microcontroller. This allows for a smaller and more compact keypad than a traditional keypad, which uses a single row and column of wires for each key. Matrix keypads are also more reliable than conventional keypads, as they are less susceptible to damage from dirt and moisture.

How does a matrix keypad work?

A matrix keypad is made up of a number of rows and columns of keys. Each key is connected to two wires, one for the row and one for the column. When a key is pressed, it completes a circuit between the row and column wires. The microcontroller can then determine which key is pressed by checking which row and column wires are connected.

Why use a matrix keypad?

There are a number of reasons why you might want to use a matrix keypad in your project. Here are a few of the most common reasons:

  • Smaller size and footprint.
  • Reliability.
  • Cost savings.

What makes my design different from most others out there?

While the matrix keypad in its simplest form is constructed from only wires and switches, that simple approach can sometimes have some unwanted effects, especially when pressing multiple keys at the same time – a phenomenon called ghosting – where you get phantom keypresses. This is easily eliminated by adding a diode in series with each switch, usually on the row connection.

That single component fixes ghosting reliably but does not come without its own problems, the most important of these being that a keypad with diodes becomes “polarised” – current can only flow in a single direction through a switch. This can cause problems with some third-party libraries, as the designer of the keypad and the designer of the library very often has quite different ideas of what a row and a column mean in a keypad.

This is important, – here we go down the rabbit hole; in my understanding of the keypad scanning routine, a column runs from top to bottom, and a row from left to right. Keeping this in mind, the microcontroller will alternatively set each column HIGH, and configure each row as an input. When a key is pressed, current will flow from the specific column GPIO, through the switch, and into the Row GPIO, sending the input pin HIGH…

It is also possible to configure the columns as inputs, with internal pullups enabled, and have each Row pin as an output, configured to sink ( pull current to ground). This will cause the specific column to go low – thus identifying the pressed key…

These different ways of handling the problem of reading a key, and believe me, there are actually more variations, create a few unique problems. We may have to swap rows and columns as far as pin connections and firmware are concerned, as well as define a custom “keymap” to assign values to each key.

The Schematic

As we can see above, the schematic is very basic. 16 switches, 16 diodes and a single 8-way header pin. Pin 1 to 4 on the header is connected to Columns 1 to 4, and Pin 5 to 8 is connected to Rows 1 to 4.

The diodes prevent “ghosting currents from flowing into other keys in a row when multiple keys are pressed together. They also seem to help with other stray signals and interference.

The PCB

The PCB is a simple double-layer board. All components are mounted on the top layer.

To limit interference from stray signals, I have routed rows and columns on opposite sides of the PCB where possible.

Manufacturing

I choose PCBWay for my PCB manufacturing.
This month, PCBWay is also celebrating its 9th anniversary, and that means that there are quite a lot of very special offers available.


Why? What makes them different from the rest?
PCBWay‘s business goal is to be the most professional PCB manufacturer for prototyping and low-volume production work in the world. With more than a decade in the business, they are committed to meeting the needs of their customers from different industries in terms of quality, delivery, cost-effectiveness and any other demanding requests. As one of the most experienced PCB manufacturers and SMT Assemblers in China, they pride themselves to be our (the Makers) best business partners, as well as good friends in every aspect of our PCB manufacturing needs. They strive to make our R&D work easy and hassle-free.

How do they do that?

PCBWay is NOT a broker. That means that they do all manufacturing and assembly themselves, cutting out all the middlemen, and saving us money.

PCBWay’s online quoting system gives a very detailed and accurate picture of all costs upfront, including components and assembly costs. This saves a lot of time and hassle.

PCBWay gives you one-on-one customer support, that answers you in 5 minutes ( from the Website chat ), or by email within a few hours ( from your personal account manager). Issues are really resolved very quickly, not that there are many anyway, but, as we are all human, it is nice to know that when a gremlin rears its head, you have someone to talk to that will do his/her best to resolve your issue as soon as possible.

Find out more here

Assembly

This project does not require a lot of specialised equipment to assemble. The SMD diodes can easily be soldered by hand, the same with the switches and 8-way header. In my case, I chose to solder the header pins on the back of the PCB, that way, I can later use the keypad in a suitable enclosure without having wires in the way.

Testing and Coding

Testing a matric keypad can sometimes be a challenge. In my case, a multimeter with clip leads, set to diode mode, with the leads connected to each column and row in turn, while minding the polarity, and pressing each key in that row in turn, verified continuity.

With that done, it was time to put my trusted Cytron Maker Uno to work, as this Arduino Clone has the added benefit of having LEDs on each of the GPIO lines, thus making it very easy to see what is happening.

I made use of a Keypad library in the Arduino IDE, mainly to cut down on the amount of coding, but also because it is easier to use a working piece of code, and then adapt that to my keypad.

Detailed Code examples for ESPHome are available on Patreon

/* @file CustomKeypad.pde
|| @version 1.0
|| @author Alexander Brevig
|| @contact alexanderbrevig@gmail.com
||
|| @description
|| | Demonstrates changing the keypad size and key values.
|| #

Edited by MakerIoT2020, with minor changes to make it function correctly with my custom keypad.
I have also added a simple LED blinking routine to show that the Arduino is “alive” and that the Keypad code seems to be NON-blocking – which is quite important to me.

*/
#include <Keypad.h>

const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns
//define the symbols on the buttons of the keypads
char hexaKeys[ROWS][COLS] = {
{‘1′,’4′,’7′,’*’},
{‘2′,’5′,’8′,’0’},
{‘3′,’6′,’9′,’#’},
{‘A’,’B’,’C’,’D’}
};
byte rowPins[ROWS] = {2,3,4,5}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {6,7,8,9}; //connect to the column pinouts of the keypad
/*
* Due to libraries being written by different people, and our definitions about
* what a row and a column are, is different, note that the rows in the code
* is actually the columns on my PCB. This becomes true, due to the fact that my
* PCB has Diodes on each switch, and that thus makes current flow in only one
* direction///
*
* it also has the “side effect” that keys are layout in a strange “mirrored” and
* rotated way in the firmware.
* it does however NOT affect the correct operation of the Keypad Module at all
*
*/

const int LEDPin = LED_BUILTIN;
int ledState = LOW;
unsigned long prevmillis = 0;
const long interval = 1000;

//initialize an instance of class NewKeypad
Keypad customKeypad = Keypad( makeKeymap(hexaKeys), rowPins, colPins, ROWS, COLS);

void setup(){
Serial.begin(115200);
pinMode(LEDPin,OUTPUT);
}

void loop(){
unsigned long currentMillis = millis();
if (currentMillis – prevmillis >= interval) {
prevmillis = currentMillis;
if (ledState == LOW) {
ledState = HIGH;
} else {
ledState = LOW;
}
digitalWrite(LEDPin,ledState);
}
char customKey = customKeypad.getKey();

if (customKey){
Serial.println(customKey);
}
}

This code works very well and allowed me to verify the correct operation of the keypad.

In conclusion

Making my own Keypad Module is a project that is long overdue. I have purchased a few online over the years, and as they were mostly of the membrane type, they did not last very long – it must be something to do with the ultra-cheap flexible PCB ribbon connector, since a quality membrane keypad can be quite expensive, and usually lasts quite a long time.

Having my own module available to experiment with will allow me to do some long-delayed improvements to many of my IoT modules. That code, mostly YAML for ESPHome, will be made available on Patreon.

Using I2C with a 4×4 Matrix Keypad

Using a matrix keypad is a very easy way to add multiple control buttons to a project, be it to enter a password, or to control different devices. These keypads do unfortunately have some serious flaws (in my view anyway)

1) They are usually of extremely low quality ( especially some of the membrane types from China). This means they dont last very long.
2) A typical 4×4 Matrix keypad will require 8 of your precious IO pins for itself.

These two flaws can however easily be solved, if we use a bit of technology, and are willing to to a bit of simple circuit construction by ourselves.

What does this mean ? Most of us makers will inevitably have a piece of proto-board or strip-board lying around, as well as a few momentary push-button switches. These can easily be used to make out own, much more reliable keypad. Let us look at the circuit

Circuit diagram for a 4×4 Matrix Keypad

As we can see, to build a 4×4 matrix keypad, we will need 16 momentary switches. These are connected together as shown above. You can then interface it with your favourite micro-controller to read the key(s) pressed…

This definitely solves the first of my problems, but we still need 8 pins to control this keypad… or do we? No, we don’t, we need only 2 pins. That is to say if we use one of those PCF8574 I2C IO port expander modules. They are much more reliable, as well as quite cheap as well. all depending on where you buy them from, and how long you are willing to wait for shipping 🙂

Let us see how to connect the keypad to the I2C Module

a 4×4 Membrane Matrix Keypad with PCF8574 I2C port expander module
Connecting the two together, note that we do not connect the INT pin
Connect Power (VCC, GND and I2C lines
Connect to Arduino or your preferred microcontroller. We have used Arduino Uno, Note that you can also connect the I2C to A4 (SDA) and A5(SCL) if you prefer.

Now, we need to install some libraries

The first one is the actual Keypad library, you can download it from the link below

The second library that we will need, is the keypad_i2c library, once again, download it from the link below.

Coding the keypad



#include <Key.h>
#include <Keypad.h>
#include <Keypad_I2C.h>

#define I2CADDR 0x26 // Set the Address of the PCF8574

const byte ROWS = 4; // Set the number of Rows
const byte COLS = 4; // Set the number of Columns

// Set the Key at Use (4x4)
char keys [ROWS] [COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

// define active Pin (4x4)
byte rowPins [ROWS] = {0, 1, 2, 3}; // Connect to Keyboard Row Pin
byte colPins [COLS] = {4, 5, 6, 7}; // Connect to Pin column of keypad.

// makeKeymap (keys): Define Keymap
// rowPins:Set Pin to Keyboard Row
// colPins: Set Pin Column of Keypad
// ROWS: Set Number of Rows.
// COLS: Set the number of Columns
// I2CADDR: Set the Address for i2C
// PCF8574: Set the number IC
Keypad_I2C keypad (makeKeymap (keys), rowPins, colPins, ROWS, COLS, I2CADDR, PCF8574);

void setup () {
  Wire .begin (); // Call the connection Wire
  keypad.begin (makeKeymap (keys)); // Call the connection
  Serial.begin (9600);

}
void loop () {
 
  char key = keypad.getKey (); // Create a variable named key of type char to hold the characters pressed
 
  if (key) {// if the key variable contains
    Serial.println (key); // output characters from Serial Monitor
  }
}

Upload this to your Arduino device and enjoy. This sketch can also be adapted for 1×4, and 4×3 keypads, and with a little modification, will also work perfectly on ESP32 or ESP8266 as well…