You need to output a signal in order to read which key is pressed. So if you make the rows outputs and columns inputs, then for each row you scan the column. If you get a high signal at y-row for x-column then a key was pressed and all you need to do is figure out which key it was.
You can use a 2D array to hold all the key characters and all you need to do is index through them when the correct key is pressed.
I think the keypad library is doing the opposite, its outputting to the columns and reading the rows.
There's a reason ppl use a library for this, it isn't a beginner's project.
Google
scan a matrix keyboard
and see if any of the hits gives you more to go on based on what you know so far. I saw many articles laying the hole thing out. I do not know if any would make the least sense to you.
Also, you could read the source code for a library scanner, here's one:
This isn't something we can talk you through a step at a time. I won't, anyway, it's just bigger than a simple answer to a simple question.
In general reading code is a good way to see how various things are accomplished.
I have had that exact same keypad for about 4 years and never got around to using it. I had nothing better to do so I wrote some code to test it. This works correctly on an Uno R3. The wiring is exactly as shown in the image in post #1.
// Define pins connected to keypad
const byte columnPins[4] = {6, 7, 8, 9};
const byte rowPins[4] = {10, 11, 12, 13};
// Define characters for each key
const char keyChars[4][4] = {{'D','#','0','*'},
{'C','9','8','7'},
{'B','6','5','4'},
{'A','3','2','1'}};
void setup() {
Serial.begin(9600);
// Set column pins to inputs with pull up
for (byte column = 0; column < sizeof(columnPins); column++) {
pinMode(columnPins[column], INPUT_PULLUP);
}
// Set row pins to high outputs
for (byte row = 0; row < sizeof(rowPins); row++) {
pinMode(rowPins[row], OUTPUT);
digitalWrite(rowPins[row], HIGH);
}
}
void loop() {
// Check each row to see if a key is pressed on that row
for (byte row = 0; row < sizeof(rowPins); row++) {
// Set the output pin for this row to LOW
digitalWrite(rowPins[row], LOW);
// Check each column of this row for a key pressed
for (byte column = 0; column < sizeof(columnPins); column++) {
if (digitalRead(columnPins[column]) == LOW) {
Serial.println("Key pressed row=" + String(row) + " column=" + String(column) + " = key " + String(keyChars[row][column]));
}
}
// Put the output for this row back to HIGH
digitalWrite(rowPins[row], HIGH);
}
// Delay to stop serial monitor getting full of text for 1 click
delay(250);
}
When a key is pressed it prints what key has been pressed to the serial monitor. This output is for key presses on key '1', then '5', then '9', then 'D'
Nice. One hopes that @gabriel_hugi can test it, and then read it carefully to match it to whatever else she learns about how the scanning magic works.
That sketch got me interested in seeing how the libraries handle debouncing and their various ways of presented keystroke activity to the programmer using them.
I know it's a PITA, but you really can get along without using Strings. I appreciate the ease of doing, but it is totally unnecessary and going without here needs no deep dive into C character arrays:
Yes I know, and do mostly avoid them in real code. I did it for a mixture of: being lazy and reducing the line count. Not particularly valid reasons.
I like this article on using Strings.