#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include "driver/adc.h"
#include "string.h"
#include <math.h>
#define BLUE_LED 8 //GPIO14
#define GREEN_LED 6 //GPIO12
#define RED_LED 7 //GPIO13
#define TEMP_ADC ADC1_CHANNEL_0 //ADC channel for thermistor
//Keypad configuration
#define ROWS 4
#define COLS 4
//Function declarations
void setupGPIO(); //Initialize GPIO pins
char scanKeypad(); //Scan keypad for key press
float readTemperature(adc1_channel_t channel, float nominalRes, float B);
bool checkPin(); //Check if entered PIN matches correct PIN
//Keypad rows and columns pins
int rowPins[ROWS] = {38, 37, 36, 35};
int colPins[COLS] = {0, 45, 48, 47};
//Keypad layout
char keys[ROWS][COLS] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
//Initializing variables
char pin[5] = {0}; //Buffer to store entered PIN (4 digits + null terminator)
int position = 0; //Current position in PIN entry
int blueCounter = 0; //Simple counter for blue LED blinking
bool blueLedState = false; //To track the state of the blue light
void app_main() {
setupGPIO(); //Initialize GPIO pins
//Configure ADC for temperature reading
adc1_config_width(ADC_WIDTH_BIT_12);
adc1_config_channel_atten(TEMP_ADC, ADC_ATTEN_DB_12);
//Prompt user to enter key code
printf("Enter Key Code:\n");
while (1) {
/* The code below allows the blue led to blink once every second
while keeping the polling rate high for high responsiveness on the keypad.
We want the toggle to occur once ever 0.5 seconds, since it has to toggle once per second. */
if (blueCounter >= 50) { //BLUE LED BLINKING - Toggle every 50 iterations at 10ms.
blueLedState = !blueLedState; //Toggles the value by reversing it's previous state.
gpio_set_level(BLUE_LED, blueLedState); //The blue led toggles and reverses its state.
blueCounter = 0; //Reset counter so that 50 iterations can occur before the next toggle.
} else {
blueCounter++; //The counter is incremented until it hits 50, then the condition above will be met.
}
// KEYPAD SCANNING - Checking for key press
char key = scanKeypad();
//If a key is pressed, then...
if (key != 0) {
//Check if it's a digit AND we haven't filled 4 digits yet
if (key >= '0' && key <= '9' && position < 4) { //Less than 4 because we only want 4 digits to be recorded (0-3).
//Store digit in PIN buffer
pin[position] = key;
position++;
printf("*");
fflush(stdout); //Flushes out the buffer and immediately prints asterisk to the same line.
// If 4 digits entered, verify PIN
if (position == 4) {
pin[4] = 0; // Null-terminate
// Check if PIN is correct
if (checkPin()) {
// ACCESS GRANTED
printf("\nAccess Granted!\n");
// Turn off blue LED.
gpio_set_level(BLUE_LED, 0);
// Turn on green LED
gpio_set_level(GREEN_LED, 1);
// Read and display temperature
float tempC = readTemperature(ADC1_CHANNEL_3, 10000.0f, 3950.0f);
printf("Temperature: %.1f° C\n", tempC);
// Keep green LED on for 5 seconds
vTaskDelay(pdMS_TO_TICKS(5000));
gpio_set_level(GREEN_LED, 0);
} else {
// ACCESS DENIED
printf("\nAccess Denied!\n");
// Turn off blue LED.
gpio_set_level(BLUE_LED, 0);
// Turn on red LED
gpio_set_level(RED_LED, 1);
// Keep red LED on for 2 seconds
vTaskDelay(pdMS_TO_TICKS(2000));
gpio_set_level(RED_LED, 0);
}
// Reset PIN buffer and position
for (int i = 0; i < 5; i++) {
pin[i] = 0; // Clear PIN buffer manually
}
position = 0;
// Prompt for next PIN
printf("\nEnter Key Code:\n");
}
}
}
// Delay for timing (10ms)
vTaskDelay(pdMS_TO_TICKS(10));
}
}
void setupGPIO() {
// Set up LEDs as outputs
gpio_set_direction(BLUE_LED, GPIO_MODE_OUTPUT);
gpio_set_direction(GREEN_LED, GPIO_MODE_OUTPUT);
gpio_set_direction(RED_LED, GPIO_MODE_OUTPUT);
// Set initial state (all off)
gpio_set_level(BLUE_LED, 0);
gpio_set_level(GREEN_LED, 0);
gpio_set_level(RED_LED, 0);
// Set up keypad rows as outputs
for (int i = 0; i < ROWS; i++) {
gpio_set_direction(rowPins[i], GPIO_MODE_OUTPUT);
gpio_set_level(rowPins[i], 1);
}
// Set up keypad columns as inputs with pull-ups
for (int i = 0; i < COLS; i++) {
gpio_set_direction(colPins[i], GPIO_MODE_INPUT);
gpio_set_pull_mode(colPins[i], GPIO_PULLUP_ONLY);
}
}
char scanKeypad() {
for (int row = 0; row < ROWS; row++) {
// Set current row LOW
gpio_set_level(rowPins[row], 0);
vTaskDelay(pdMS_TO_TICKS(5));
// Check each column
for (int col = 0; col < COLS; col++) {
// If column is LOW, a key is pressed
if (gpio_get_level(colPins[col]) == 0) {
char keyPressed = keys[row][col];
// Wait for key release (debounce)
while (gpio_get_level(colPins[col]) == 0) {
vTaskDelay(pdMS_TO_TICKS(50));
}
// Set row back to HIGH
gpio_set_level(rowPins[row], 1);
// Return the key value
return keyPressed;
}
}
// Set row back to HIGH
gpio_set_level(rowPins[row], 1);
vTaskDelay(pdMS_TO_TICKS(5));
}
return 0; // No key pressed
}
bool checkPin() {
// Manual comparison instead of using strcmp
if (pin[0] == '1' &&
pin[1] == '2' &&
pin[2] == '3' &&
pin[3] == '4') {
return true;
} else {
return false;
}
}
float readTemperature(adc1_channel_t channel, float nominalRes, float B) {
// Variable declarations
float T1 = 25 + 273.15; // Reference temperature in Kelvin (25°C)
float voltage, resistance, temperature;
// Read ADC from the specified channel (use the parameter!)
int adc_value = adc1_get_raw(channel);
// Debug raw ADC value
printf("Raw ADC Value: %d (0-4095)\n", adc_value);
// Convert ADC to voltage (assuming 3.3V reference)
voltage = adc_value * 3.3f / 4095.0f;
// Calculate thermistor resistance using voltage divider formula
// Assuming thermistor is connected to VCC (original circuit configuration)
resistance = nominalRes * (3.3f / voltage - 1.0f);
// Calculate temperature using the B-parameter equation
// T = 1 / (1/T₀ + (1/B) * ln(R/R₀))
temperature = 1.0f / ((1.0f / T1) + (logf(resistance / nominalRes) / B));
// Convert from Kelvin to Celsius
float celsius = temperature - 273.15f;
// Debug output
printf("\nADC: %d | Voltage: %.2fV | Resistance: %.2fΩ | Temperature: %.2f°C\n",
adc_value, voltage, resistance, celsius);
return celsius;
}