#include <Adafruit_Fingerprint.h>
#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include "esp_sleep.h"
const char *ssid = "Wokwi-GUEST";
const char *password = "";
const char *serverAddress = "www.google.com";
AsyncWebServer server(80);
// Initialize the fingerprint sensor using Serial2
Adafruit_Fingerprint finger = Adafruit_Fingerprint(&Serial2);
const int redLedPin = 15; // System is active indicator
const int greenLedPin = 2; // Internet connectivity status
const int buzzerPin = 12; // Buzzer pin
// Power Logic
const int batteryPin = 36; // For Battery input into MCU
const int chargingPin = 39; // For Charging Battery input into MCU
const int sensorPwrPin = 23; // Sensor Pin
const float maxBatteryVoltage = 5125.15; // Max battery voltage (4.2)
void setup()
{
Serial.begin(115200); // Initialize the primary serial interface for debugging
// Initialize the secondary serial interface for the fingerprint sensor
Serial2.begin(57600);
// Connect to Wi-Fi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED)
{
delay(3000);
Serial.println("Connecting to WiFi... " + String(ssid) + String(password));
Serial.println("Connecting to WiFi...");
}
Serial.print("Connected to WiFi. Local IP address: ");
Serial.println(WiFi.localIP());
// Define endpoint for enrollment request
server.on("/enrollStudent", HTTP_POST, handleEnrollmentRequest);
server.on("/deleteEnrolledStudent", HTTP_POST, handleDeleteEnrolledStudentRequest);
server.on("/studentAttendance", HTTP_POST, handleAttendanceRequest);
//
pinMode(batteryPin, INPUT);
pinMode(chargingPin, INPUT);
pinMode(sensorPwrPin, OUTPUT);
turnSensorOff(); // Turn sensor off initially
// Set buzzer pin
pinMode(buzzerPin, OUTPUT);
digitalWrite(buzzerPin, LOW);
// Set green LED pin
pinMode(greenLedPin, OUTPUT);
digitalWrite(greenLedPin, LOW);
// Set red LED pin
pinMode(redLedPin, OUTPUT);
digitalWrite(redLedPin, LOW);
server.begin();
}
// Check Wi-Fi and Internet Connectivity
void loop()
{
// Example: Set the green LED based on connectivity status
if (WiFi.status() != WL_CONNECTED)
{
// Device is not connected to the WI-FI
blinkLED(greenLedPin, 500, 1); // Blink continuously
}
else
{
// Device is connected to Wi-Fi, check internet connection
if (isConnectedToInternet())
{
// Device is connected to the internet
fadeLED(greenLedPin, 1000); // Slowly fade in and out
}
else
{
// Device is connected to Wi-Fi but not the internet
blinkLED(greenLedPin, 200, 5); // Blink rapidly
}
}
// Check Battery Voltage
CheckBatteryVoltageLevel();
}
// Function to check internet connectivity
bool isConnectedToInternet() {
if (WiFi.status() != WL_CONNECTED) {
return false;
}
WiFiClient client;
if (client.connect("www.google.com", 80)) {
client.stop();
return true;
}
return false;
}
// Function to control the green LED for blinking
void blinkLED(int pin, int duration, int count)
{
for (int i = 0; i < count; ++i)
{
digitalWrite(pin, HIGH);
delay(duration);
digitalWrite(pin, LOW);
delay(duration);
}
}
// Function to control the green LED for fading in and out
void fadeLED(int pin, int duration)
{
analogWrite(pin, 0); // Start with LED off
for (int brightness = 0; brightness <= 255; ++brightness)
{
analogWrite(pin, brightness);
delay(duration / 255);
}
for (int brightness = 255; brightness >= 0; --brightness)
{
analogWrite(pin, brightness);
delay(duration / 255);
}
}
// Function to control the buzzer based on different modes
void buzzerControl(int beepCount, int beepDuration)
{
for (int i = 0; i < beepCount; ++i)
{
digitalWrite(buzzerPin, HIGH);
delay(beepDuration);
digitalWrite(buzzerPin, LOW);
delay(beepDuration);
}
}
void turnSensorOff()
{
digitalWrite(sensorPwrPin, LOW);
}
void turnSensorOn()
{
digitalWrite(sensorPwrPin, HIGH);
delay(1000);
finger.begin(57600);
delay(5);
if (finger.verifyPassword())
{
buzzerControl(2, 200);
}
else
{
buzzerControl(1, 200);
}
}
// Logic to check power level and change ESP state
void CheckBatteryVoltageLevel()
{
float currentBatteryVoltage = (((analogRead(batteryPin) * 4.2) / 4095)); // +0.22;
Serial.print("Battery Voltage: ");
Serial.println(currentBatteryVoltage);
// Check if the battery voltage is less than 2.8V
if (currentBatteryVoltage < 2.8) {
Serial.println("Battery voltage is below 2.8V. Putting ESP32 to sleep.");
// Configure the sleep parameters
esp_sleep_enable_timer_wakeup(90 * 1000000ULL); // Sleep for 90 seconds
// Enter deep sleep mode
esp_deep_sleep_start();
Serial.println("This will never be printed");
// TODO: Send a battery low response to the frontend
}
// Check if the battery voltage is greater than or equal to the maximum voltage
if (currentBatteryVoltage >= maxBatteryVoltage) {
Serial.println("Battery is fully charged. Stopping charging.");
// Stop charging by setting the chargingPin LOW
digitalWrite(chargingPin, LOW);
// TODO: Send a battery full response to the frontend
} else {
// Continue charging by setting the chargingPin HIGH
digitalWrite(chargingPin, HIGH);
}
delay(1000); // Adjust the delay based on your application
}
// Enrollment
// HTTP request handler for "/enrollStudent"
void handleEnrollmentRequest(AsyncWebServerRequest *request)
{
Serial.println("Received a request!");
if (request->method() == HTTP_POST)
{
String studentId;
Serial.println("Enroll endpoint triggered");
// Read studentId from the request body
if (request->hasParam("studentId", true))
{
studentId = request->getParam("studentId", true)->value();
}
else
{
request->send(400, "text/plain", "Bad Request");
return;
}
// Enroll the Student with the fingerprint sensor
int enrollmentStatus = enrollStudent(studentId);
if (enrollmentStatus)
{
// Send response back to the API route
request->send(200, "application/json", "{\"fingerprintId\":" + String(studentId) + ", \"message\":\"Fingerprint enrolled successfully\"}");
}
else
{
request->send(500, "text/plain", "Enrollment failed");
}
}
else
{
request->send(405, "text/plain", "Method Not Allowed");
}
}
// Enroll Function
bool enrollStudent(const String &studentId)
{
// Get the Fingerprint sensor ready
turnSensorOn();
delay(1000);
buzzerControl(2, 200);
// Check if the sensor contains any fingerprint data
finger.getTemplateCount();
if (finger.templateCount == 0)
{
Serial.println("Sensor doesn't contain any fingerprint data. Please run the 'enroll' example.");
return false;
}
// Capture first fingerprint image
int result = finger.getImage();
if (result != FINGERPRINT_OK)
{
buzzerControl(3, 200);
return false;
}
// Convert the first fingerprint image to template
result = finger.image2Tz(1);
if (result != FINGERPRINT_OK)
{
buzzerControl(3, 200);
return false;
}
// Signal to remove Finger
buzzerControl(2, 200);
delay(2000);
// Wait for no finger on the sensor
while (finger.getImage() != FINGERPRINT_NOFINGER)
{
// Do nothing
}
// Capture the second fingerprint image
result = finger.getImage();
if (result != FINGERPRINT_OK)
{
buzzerControl(3, 200);
return false;
}
// Convert the second fingerprint image to template
result = finger.image2Tz(2);
if (result != FINGERPRINT_OK)
{
buzzerControl(3, 200);
return false;
}
// Fingerprint successfully capture
buzzerControl(2, 200);
delay(2000);
// Create fingerprint model
result = finger.createModel();
if (result != FINGERPRINT_OK)
{
buzzerControl(3, 200);
return false;
}
// Store the fingerprint model
result = finger.storeModel(static_cast<uint16_t>(studentId.toInt()));
if (result != FINGERPRINT_OK)
{
buzzerControl(3, 200);
return false;
}
// Fingerprint successfully capture
buzzerControl(2, 200);
return true;
}
// Attendance
// HTTP request handler for "/attendance"
void handleAttendanceRequest(AsyncWebServerRequest *request)
{
if (request->method() == HTTP_POST)
{
// Get the Fingerprint sensor ready
turnSensorOn();
delay(1000);
buzzerControl(2, 200);
// Perform attendance marking
bool attendanceMarkedForstudentId = markAttendance();
// Send a response back to the client based on the attendance marking result
if (attendanceMarkedForstudentId)
{
// Successful attendance marking beep
buzzerControl(2, 200);
request->send(200, "application/json", "{\"message\":\"Attendance marked successfully\"}");
}
else
{
// Attendance marking failed beep
buzzerControl(3, 200);
request->send(400, "application/json", "{\"message\":\"Attendance marking failed\"}");
}
}
else
{
request->send(405, "text/plain", "Method Not Allowed");
}
}
// Get ID for Attendance
bool markAttendance()
{
uint8_t p = finger.getImage();
switch (p)
{
case FINGERPRINT_OK:
buzzerControl(1, 200);
Serial.println("Image taken");
break;
case FINGERPRINT_NOFINGER:
Serial.println("No finger detected");
return false;
case FINGERPRINT_PACKETRECIEVEERR:
buzzerControl(3, 200);
Serial.println("Communication error");
return false;
case FINGERPRINT_IMAGEFAIL:
buzzerControl(3, 200);
Serial.println("Imaging error");
return false;
default:
buzzerControl(3, 200);
Serial.println("Unknown error");
return false;
}
// OK success!
p = finger.image2Tz();
switch (p)
{
case FINGERPRINT_OK:
Serial.println("Image converted");
break;
case FINGERPRINT_IMAGEMESS:
buzzerControl(3, 200);
return false;
Serial.println("Image too messy");
case FINGERPRINT_PACKETRECIEVEERR:
buzzerControl(3, 200);
return false;
Serial.println("Communication error");
case FINGERPRINT_FEATUREFAIL:
buzzerControl(3, 200);
return false;
Serial.println("Could not find fingerprint features");
case FINGERPRINT_INVALIDIMAGE:
buzzerControl(3, 200);
return false;
Serial.println("Could not find fingerprint features");
default:
buzzerControl(3, 200);
return false;
Serial.println("Unknown error");
}
// OK converted!
p = finger.fingerSearch();
if (p == FINGERPRINT_OK)
{
return true;
}
else if (p == FINGERPRINT_PACKETRECIEVEERR)
{
buzzerControl(3, 200);
return false;
Serial.println("Communication error");
}
else if (p == FINGERPRINT_NOTFOUND)
{
buzzerControl(3, 200);
return false;
Serial.println("Did not find a match");
}
else
{
buzzerControl(3, 200);
return false;
Serial.println("Unknown error");
}
}
// HTTP request handler for "/deleteEnrolledStudent"
void handleDeleteEnrolledStudentRequest(AsyncWebServerRequest *request)
{
Serial.println("Received a request!");
if (request->method() == HTTP_POST)
{
String studentId;
Serial.println("Delete Enrolled Student endpoint triggered");
// Read studentId from the request body
if (request->hasParam("studentId", true))
{
studentId = request->getParam("studentId", true)->value();
}
else
{
request->send(400, "text/plain", "Bad Request");
return;
}
// Delete enrolled User
// Convert studentId to uint8_t
uint8_t studentIdAsNumber = studentId.toInt();
bool fingerprintDeleted = deleteFingerprint(studentIdAsNumber);
// Send a response back to the client based on the attendance marking result
if (fingerprintDeleted)
{
// Successful attendance marking beep
buzzerControl(2, 200);
request->send(200, "application/json", "{\"message\":\"Fingerprint deleted successfully\"}");
}
else
{
// Attendance marking failed beep
buzzerControl(3, 200);
request->send(400, "application/json", "{\"message\":\"Fingerprint deletion failed\"}");
}
}
}
// Delete Enrolled Student
bool deleteFingerprint(const uint8_t &studentId)
{
uint8_t p = -1;
p = finger.deleteModel(studentId);
if (p == FINGERPRINT_OK)
{
buzzerControl(2, 200);
// TODO: Send a success response to the frontend // Send response back to the API route
Serial.println("Deleted!");
return true;
}
else if (p == FINGERPRINT_PACKETRECIEVEERR)
{
buzzerControl(3, 200);
// TODO: Send a Communication error response to the frontend
Serial.println("Communication error");
}
else if (p == FINGERPRINT_BADLOCATION)
{
buzzerControl(3, 200);
// TODO: Send a Could not delete in that location response to the frontend
Serial.println("Could not delete in that location");
}
else if (p == FINGERPRINT_FLASHERR)
{
buzzerControl(3, 200);
// TODO: Send a Error writing to flash response to the frontend
Serial.println("Error writing to flash");
}
else
{
buzzerControl(3, 200);
Serial.println("Unknown error");
}
return false;
}