#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_log.h"
//Declaring functions (JI)
void setup_gpio(); //This function initializes outputs and inputs. (JI)
char scan_keypad(); //This function continuously scans the keypad and responds on user input. (JI)
char readKeyPadSingleScan(char _keys[4][4], int _scanColPins[4], int _rowPins[4]); //Declaration for the non-blocking keypad scan function that performs a single scan. (JI)
//Defining the matrix for the keypad (4x4). (JI)
#define ROWS 4
#define COLS 4
//Declaring arrays with GPIO pins for the rows and columns. (JI)
int scanRowPins[ROWS] = {38, 37, 36, 35};
int colPins[COLS] = {0, 45, 48, 47};
//Declaring a two-dimensional array using the rows and columns arrays declared above. (JI)
char keys[ROWS][COLS] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
void app_main() {
setup_gpio();
int counter = 0; //Counter is used to prove non-blocking polling is functional. (JI)
while (1) {
//Calling the non-blocking polling function. (JI)
char key = readKeyPadSingleScan(keys, colPins, scanRowPins);
//This counter will increment regardless of whether a key is pressed. Counter is a verification to prove non-blocking polling is working. (JI)
printf("Counter: %d\n", counter++);
if (key != '\0') { //If the values returned from the keypad are NOT null, then proceed with the code below. (JI)
printf("You pressed: %c\n", key); //Print the button pressed by printing the value of 'key'. (JI)
}
vTaskDelay(pdMS_TO_TICKS(100));
}
}
//Function definition to initialize rows and columns as outputs and inputs. (JI)
void setup_gpio() {
for (int i = 0; i < ROWS; i++) {
gpio_set_direction(scanRowPins[i], GPIO_MODE_OUTPUT); //Initializing rows as outputs. (JI)
gpio_set_level(scanRowPins[i], 1);
}
for (int i = 0; i < COLS; i++) {
gpio_set_direction(colPins[i], GPIO_MODE_INPUT); //Initializing columns as inputs. (JI)
gpio_set_pull_mode(colPins[i], GPIO_PULLUP_ONLY);
}
}
//Function definition for the keypad scanning. (JI)
char scan_keypad() {
uint8_t scanVal; //Declaring an unsigned 8-bit variable. The value of the button pressed will be stored in this variable. (JI)
for (int i = 0; i < ROWS; i++) { //Scan through the each row, one at a time to get their values. (JI)
scanVal = ~(1 << i); //One's complement the value of each row. If a button is pressed, only one row will be different. One's complement is required because the circuit is in a pull-up resistor configuration. (JI)
//This portion takes the newly updated value of the button press from scanVal, and iterates through each bit to read its binary value. (JI)
for (int j = 0; j < ROWS; j++) {
gpio_set_level(scanRowPins[j], (scanVal >> j) & 1); //Here, the button pressed is identified by extracting the bits from each row. The row with a zero in one of the bits means that specific button was pressed in that row. (JI)
}
vTaskDelay(pdMS_TO_TICKS(5));
//Debounce operation to prevent accidental additional inputs or errors. (JI)
for (int k = 0; k < COLS; k++) { //Iterate through each column. (JI)
if (gpio_get_level(colPins[k]) == 0) { //If any of the columns detect a '0', it means a button is being pressed. (JI)
while (gpio_get_level(colPins[k]) == 0) { //While that button is being pressed, continuously repeat a 50ms delay until the button is depressed. (JI)
vTaskDelay(pdMS_TO_TICKS(50));
}
return keys[i][k]; //Return the values of the intersecting column and row. These values will be stored in the 'key' variable in app_main. (JI)
}
}
}
return '\0';
}
//Function definition for non-blocking polling. (JI)
char readKeyPadSingleScan(char _keys[4][4], int _scanColPins[4], int _rowPins[4]) {
uint8_t scanVal;
char retVal = '\0'; // Setting default return value to null character when no key is pressed. (JI)
//Debug print statement to show when scanning is happening. (JI)
printf("Performing single non-blocking keypad scan...\n");
//Scanning each row just like in scan_keypad(). (JI)
for (int i = 0; i < ROWS; i++) {
//Creating the binary value to set one row LOW at a time. (JI)
scanVal = ~(1 << i);
//Setting the GPIO levels based on scanVal value. (JI)
for (int j = 0; j < ROWS; j++) {
gpio_set_level(_rowPins[j], (scanVal >> j) & 1);
}
vTaskDelay(pdMS_TO_TICKS(5));
//Checking each column for a pressed button. (JI)
for (int k = 0; k < COLS; k++) {
if (gpio_get_level(_scanColPins[k]) == 0) { //Return immediately when a key is detected. (JI)
retVal = _keys[i][k]; //Update retVal with the value of the _keys. (JI)
return retVal;
}
}
}
//If no key was pressed, return the null character. (JI)
return retVal;
}