#define BLYNK_TEMPLATE_ID "TMPL3LCF0aea1"
#define BLYNK_TEMPLATE_NAME "internship "
#define BLYNK_AUTH_TOKEN "YW30QNhFxiHR_pwD24_bWamVUoK7Hnq7"
// --- Included Libraries ---
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>
#include <WiFi.h>
#include <BlynkSimpleEsp32.h>
#include <ESP32Servo.h>
// --- Blynk Configuration ---
char auth[] = BLYNK_AUTH_TOKEN;
char ssid[] = "Wokwi-GUEST";
char pass[] = "";
// Initialize the I2C LCD
LiquidCrystal_I2C lcd(0x27, 20, 4);
// --- Passwords ---
const String adminPassword = "admin";
const String userPassword = "user";
// --- Global State Variables ---
int attemptCount = 0;
bool isLocked = false;
unsigned long lockTime = 0;
unsigned long lastMotionTime = 0;
bool lcdActive = false;
bool resetPressed = false;
// --- Constants ---
const unsigned long LCD_TIMEOUT = 10000;
const int EEPROM_SIZE = 512;
const int NUM_BOOKS = 3;
// --- Timer Variables ---
const unsigned long RENEWAL_PERIOD = 120000; // 2 minutes in milliseconds
unsigned long lastRenewTimes[NUM_BOOKS];
// --- EEPROM Address Definitions ---
const int ADDR_BOOK_DETAILS = 0;
const int ADDR_USER_MSG = 100;
const int ADDR_ADMIN_MSG = 200;
const int ADDR_BOOK1_NAME = 250;
const int ADDR_BOOK2_NAME = 280;
const int ADDR_BOOK3_NAME = 310;
const int ADDR_BOOK1_RENEW_TIME = 400;
const int ADDR_BOOK2_RENEW_TIME = 404;
const int ADDR_BOOK3_RENEW_TIME = 408;
// --- Book Inventory Management ---
const int NUM_SUBJECTS = 6;
const int MAX_USER_BOOKS = 3;
struct LibraryBook {
char name[6];
int count;
};
LibraryBook libraryStock[NUM_SUBJECTS];
int userBorrowedBooks[MAX_USER_BOOKS];
// --- CRITICAL FIX: CORRECTED EEPROM ADDRESSES TO PREVENT OVERLAP ---
const int ADDR_LIB_STOCK = 412;
const int ADDR_USER_BOOKS = 485;
const int ADDR_EEPROM_VERSION = 510;
const int CURRENT_EEPROM_VERSION = 2;
// Array to hold personal book names in memory for easy access
String bookNames[NUM_BOOKS];
// --- Pin Definitions (ESP32 GPIO) ---
const int buzzerPin = 15;
const int redLEDPin = 2;
const int greenLEDPin = 4;
const int pirPin = 19;
const int resetButtonPin = 23;
// --- NEW: Servo Pin Definitions ---
const int issueServoPin = 25;
const int returnServoPin = 26;
// --- NEW: Servo Objects ---
Servo issueServo;
Servo returnServo;
// --- Helper Functions for EEPROM ---
void readStringFromEEPROM(int addr, char* buffer, int len) {
for (int i = 0; i < len; i++) {
buffer[i] = EEPROM.read(addr + i);
if (buffer[i] == '\0') return;
}
buffer[len - 1] = '\0';
}
void writeStringToEEPROM(int addr, const String& str) {
for(int i = 0; i < 30; i++) EEPROM.write(addr + i, 0);
for (int i = 0; i < str.length() && i < 29; i++) EEPROM.write(addr + i, str[i]);
EEPROM.write(addr + str.length(), '\0');
}
// Function to initialize the library stock for the first time
void initializeLibraryStock() {
Serial.println("EEPROM data invalid or outdated! Initializing library stock...");
strcpy(libraryStock[0].name, "AE"); libraryStock[0].count = 2;
strcpy(libraryStock[1].name, "DE"); libraryStock[1].count = 3;
strcpy(libraryStock[2].name, "EE"); libraryStock[2].count = 2;
strcpy(libraryStock[3].name, "MPMC"); libraryStock[3].count = 2;
strcpy(libraryStock[4].name, "EMTL"); libraryStock[4].count = 1;
strcpy(libraryStock[5].name, "AME"); libraryStock[5].count = 4;
for (int i = 0; i < MAX_USER_BOOKS; i++) userBorrowedBooks[i] = -1;
EEPROM.put(ADDR_LIB_STOCK, libraryStock);
EEPROM.put(ADDR_USER_BOOKS, userBorrowedBooks);
EEPROM.write(ADDR_EEPROM_VERSION, CURRENT_EEPROM_VERSION);
EEPROM.commit();
Serial.println("Library stock has been initialized and saved to EEPROM.");
}
// Function to load all necessary data at startup
void loadDataFromEEPROM() {
if (EEPROM.read(ADDR_EEPROM_VERSION) != CURRENT_EEPROM_VERSION) {
initializeLibraryStock();
} else {
Serial.println("Loading book stock from EEPROM...");
EEPROM.get(ADDR_LIB_STOCK, libraryStock);
EEPROM.get(ADDR_USER_BOOKS, userBorrowedBooks);
Serial.println("Load complete.");
}
char buffer[31];
int book_name_addrs[] = {ADDR_BOOK1_NAME, ADDR_BOOK2_NAME, ADDR_BOOK3_NAME};
int renew_time_addrs[] = {ADDR_BOOK1_RENEW_TIME, ADDR_BOOK2_RENEW_TIME, ADDR_BOOK3_RENEW_TIME};
for(int i = 0; i < NUM_BOOKS; i++) {
readStringFromEEPROM(book_name_addrs[i], buffer, 30);
bookNames[i] = String(buffer);
if (bookNames[i].length() == 0 || bookNames[i] == " ") {
bookNames[i] = "Book" + String(i+1);
writeStringToEEPROM(book_name_addrs[i], bookNames[i]);
}
EEPROM.get(renew_time_addrs[i], lastRenewTimes[i]);
if (lastRenewTimes[i] == 0 || lastRenewTimes[i] > millis()) {
lastRenewTimes[i] = millis();
EEPROM.put(renew_time_addrs[i], lastRenewTimes[i]);
}
}
EEPROM.commit();
}
// --- NEW: Function to control servo motors ---
void issueServos() {
Serial.println("Activating servos...");
issueServo.write(90);
delay(2000); // Hold for 2 seconds
issueServo.write(0);
Serial.println("Servo returned to base position.");
}
void returnServos() {
Serial.println("Activating servos...");
returnServo.write(90);
delay(2000); // Hold for 2 seconds
returnServo.write(0);
Serial.println("Servo returned to base position.");
}
void setup() {
Serial.begin(115200);
EEPROM.begin(EEPROM_SIZE);
loadDataFromEEPROM();
lcd.init();
lcd.backlight();
lcd.clear();
pinMode(buzzerPin, OUTPUT);
pinMode(redLEDPin, OUTPUT);
pinMode(greenLEDPin, OUTPUT);
pinMode(pirPin, INPUT);
pinMode(resetButtonPin, INPUT_PULLUP);
// --- NEW: Initialize Servos ---
issueServo.attach(issueServoPin);
returnServo.attach(returnServoPin);
issueServo.write(0);
returnServo.write(0);
Serial.println("Servos Initialized.");
Blynk.begin(auth, ssid, pass);
lcd.noBacklight();
lcdActive = false;
lcd.setCursor(1, 1);
lcd.print("Waiting for motion");
}
// --- MODIFIED: issueNewBook function with fixed LCD layout ---
void issueNewBook() {
lcd.clear();
Serial.println("\nAvailable Books (Select Number):");
int borrowedCount = 0;
for (int i = 0; i < MAX_USER_BOOKS; i++) {
if (userBorrowedBooks[i] != -1) borrowedCount++;
}
if (borrowedCount >= MAX_USER_BOOKS) {
lcd.clear();
lcd.setCursor(0, 1);
lcd.print("Borrow limit reached!");
Serial.println("Borrow limit reached!");
delay(2000);
return;
}
// Serial output remains the same
for (int i = 0; i < NUM_SUBJECTS; i++) {
Serial.println(String(i + 1) + "." + libraryStock[i].name + " - " + libraryStock[i].count + " books");
}
// --- NEW: Constant 2-column layout for LCD ---
lcd.setCursor(0, 0);
lcd.print("Available Books:");
int displayCount = 0;
for (int i = 0; i < NUM_SUBJECTS; i++) {
// Only display books that are in stock
if (libraryStock[i].count > 0) {
// Calculate position: column 0 or 10, row 1, 2, or 3
int col = (displayCount % 2 == 0) ? 0 : 10;
int row = (displayCount / 2) + 1;
if (row > 3) continue; // Prevents writing past the last LCD line
lcd.setCursor(col, row);
lcd.print(String(i + 1) + "." + libraryStock[i].name);
displayCount++;
}
}
String choiceStr = waitForInput();
if (resetPressed) return;
int choice = choiceStr.toInt() - 1;
if (choice >= 0 && choice < NUM_SUBJECTS) {
if (libraryStock[choice].count > 0) {
libraryStock[choice].count--;
userBorrowedBooks[borrowedCount] = choice;
EEPROM.put(ADDR_LIB_STOCK, libraryStock);
EEPROM.put(ADDR_USER_BOOKS, userBorrowedBooks);
EEPROM.commit();
lcd.clear();
lcd.setCursor(0, 1);
lcd.print(String(libraryStock[choice].name) + " Issued!");
Serial.println(String(libraryStock[choice].name) + " Issued!");
issueServos();
delay(1000);
} else {
lcd.clear();
lcd.setCursor(0, 1);
lcd.print("Book not available.");
Serial.println("Book not available.");
delay(2000);
}
} else {
lcd.clear();
lcd.setCursor(2, 1);
lcd.print("Invalid Choice");
delay(1500);
}
}
// --- MODIFIED: returnExistingBook function with bug fix ---
void returnExistingBook() {
lcd.clear();
Serial.println("\nYour Books (Select Number to Return):");
// --- FIX: Correctly calculate the number of borrowed books first ---
int borrowedCount = 0;
for (int i = 0; i < MAX_USER_BOOKS; i++) {
if (userBorrowedBooks[i] != -1) {
borrowedCount++;
}
}
if (borrowedCount == 0) {
lcd.clear();
lcd.setCursor(0, 1);
lcd.print("No books to return.");
Serial.println("No books to return.");
delay(2000);
return;
}
// Display borrowed books on Serial and LCD
int displayIndex = 1;
int lcdLine = 0;
for (int i = 0; i < MAX_USER_BOOKS; i++) {
if (userBorrowedBooks[i] != -1) {
int bookIndex = userBorrowedBooks[i];
String bookName = libraryStock[bookIndex].name;
Serial.println(String(displayIndex) + "." + bookName);
if (displayIndex <= 2) {
lcd.setCursor(0, lcdLine++);
lcd.print(String(displayIndex) + ". " + bookName);
} else {
lcd.setCursor(10, lcdLine - 2);
lcd.print(String(displayIndex) + ". " + bookName);
}
displayIndex++;
}
}
String choiceStr = waitForInput();
if (resetPressed) return;
int choice = choiceStr.toInt();
if (choice > 0 && choice <= borrowedCount) {
int targetUserBookIndex = -1;
int currentChoice = 0;
for (int i = 0; i < MAX_USER_BOOKS; i++) {
if (userBorrowedBooks[i] != -1) {
currentChoice++;
if (currentChoice == choice) {
targetUserBookIndex = i;
break;
}
}
}
if (targetUserBookIndex != -1) {
int bookStockIndex = userBorrowedBooks[targetUserBookIndex];
libraryStock[bookStockIndex].count++;
userBorrowedBooks[targetUserBookIndex] = -1;
int tempUserBooks[MAX_USER_BOOKS];
for (int i = 0; i < MAX_USER_BOOKS; i++) tempUserBooks[i] = -1;
int k = 0;
for (int i = 0; i < MAX_USER_BOOKS; i++) {
if (userBorrowedBooks[i] != -1) {
tempUserBooks[k++] = userBorrowedBooks[i];
}
}
memcpy(userBorrowedBooks, tempUserBooks, sizeof(userBorrowedBooks));
EEPROM.put(ADDR_LIB_STOCK, libraryStock);
EEPROM.put(ADDR_USER_BOOKS, userBorrowedBooks);
EEPROM.commit();
lcd.clear();
lcd.setCursor(0, 1);
lcd.print(String(libraryStock[bookStockIndex].name) + " Returned!");
Serial.println(String(libraryStock[bookStockIndex].name) + " Returned!");
returnServos(); // --- NEW: Activate servos on success
delay(1000);
}
} else {
lcd.clear();
lcd.setCursor(2, 1);
lcd.print("Invalid Choice");
delay(1500);
}
}
// --- MODIFIED: viewBookStockAdmin function with fixed LCD layout ---
void viewBookStockAdmin() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Library Stock:");
Serial.println("\n--- Library Stock ---");
// Serial output remains the same
for (int i = 0; i < NUM_SUBJECTS; i++) {
Serial.println(String(libraryStock[i].name) + ": " + String(libraryStock[i].count));
}
// --- NEW: Constant 2-column layout for LCD ---
for (int i = 0; i < NUM_SUBJECTS; i++) {
// Calculate position: column 0 or 10, row 1, 2, or 3
int col = (i % 2 == 0) ? 0 : 10;
int row = (i / 2) + 1;
if (row > 3) break; // Prevents writing past the last LCD line
lcd.setCursor(col, row);
// Format as "1.AE:2" to fit nicely
String entry = String(i + 1) + "." + libraryStock[i].name;
lcd.print(entry);
}
// Give time to read
delay(3000);
}
void editSubMenuAdmin() {
lcd.clear();
lcd.setCursor(0,0); lcd.print("1.Edit Details");
lcd.setCursor(0,1); lcd.print("2.Edit Books");
lcd.setCursor(0,2); lcd.print("3.Renew Book");
lcd.setCursor(0,3); lcd.print("4.View Stock");
Serial.println("Enter option: 1=Edit Details, 2=Edit Books, 3=Renew, 4.View Stock");
String j = waitForInput();
if (resetPressed) return;
if(j == "1") {
lcd.clear(); lcd.print("Enter new details:");
Serial.println("Enter new details in Serial Monitor:");
String d = waitForInput(); if(resetPressed) return;
writeStringToEEPROM(ADDR_BOOK_DETAILS, d);
EEPROM.commit();
lcd.clear(); lcd.setCursor(5,1); lcd.print("Updated!");
delay(1500);
} else if(j == "2") {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Select book to edit:");
for(int i = 0; i < NUM_BOOKS; i++) {
lcd.setCursor(0, i + 1);
String displayStr = String(i + 1) + ". " + bookNames[i].substring(0, 10);
lcd.print(displayStr);
}
Serial.println("Select book to edit (1-" + String(NUM_BOOKS) + "):");
String bookChoice = waitForInput();
int bookIndex = bookChoice.toInt() - 1;
if (bookIndex >= 0 && bookIndex < NUM_BOOKS) {
lcd.clear();
lcd.setCursor(0, 1);
lcd.print("Enter new name:");
Serial.println("Enter new name (max 10 chars):");
String newName = waitForInput();
if(resetPressed) return;
const int book_addr[] = {ADDR_BOOK1_NAME, ADDR_BOOK2_NAME, ADDR_BOOK3_NAME};
writeStringToEEPROM(book_addr[bookIndex], newName);
EEPROM.commit();
bookNames[bookIndex] = newName;
lcd.clear();
lcd.setCursor(5,1);
lcd.print("Updated!");
delay(1500);
} else {
lcd.clear(); lcd.setCursor(2,1); lcd.print("Invalid Choice"); delay(1500);
}
} else if (j == "3") {
renewBookMenu("Admin");
} else if (j == "4") {
viewBookStockAdmin();
}
}
void loop() {
Blynk.run();
checkReset();
if (resetPressed) {
resetPressed = false;
isLocked = false;
attemptCount = 0;
lcd.backlight();
lcdActive = true;
lastMotionTime = millis();
showWelcome();
while (Serial.available()) Serial.read();
return;
}
String due_events[] = {"book1_due", "book2_due", "book3_due"};
int renew_time_addrs[] = {ADDR_BOOK1_RENEW_TIME, ADDR_BOOK2_RENEW_TIME, ADDR_BOOK3_RENEW_TIME};
for(int i = 0; i < NUM_BOOKS; i++) {
if (millis() - lastRenewTimes[i] > RENEWAL_PERIOD) {
Serial.println("Renewal period for " + bookNames[i] + " expired! Sending notification.");
Blynk.logEvent(due_events[i]);
lastRenewTimes[i] = millis();
EEPROM.put(renew_time_addrs[i], lastRenewTimes[i]);
EEPROM.commit();
}
}
if (digitalRead(pirPin) == HIGH) {
lastMotionTime = millis();
if (!lcdActive) {
lcd.backlight();
lcdActive = true;
showWelcome();
}
}
if (lcdActive && (millis() - lastMotionTime > LCD_TIMEOUT)) {
lcd.noBacklight();
lcd.clear();
lcdActive = false;
}
if (!lcdActive) {
return;
}
if (isLocked) {
if (millis() - lockTime < 10000) {
lcd.clear();
lcd.setCursor(3, 1); lcd.print("System Locked");
lcd.setCursor(4, 2); lcd.print("Wait 10 sec");
return;
} else {
isLocked = false;
attemptCount = 0;
showWelcome();
}
}
if (Serial.available()) {
String choice = Serial.readStringUntil('\n');
choice.trim();
if (choice == "1") {
userLogin();
} else if (choice == "2") {
adminLogin();
} else if (!choice.isEmpty()){
lcd.clear();
lcd.setCursor(3, 1);
lcd.print("Invalid Option");
delay(1500);
showWelcome();
}
}
}
void checkReset() {
if (digitalRead(resetButtonPin) == LOW) {
delay(50);
if (digitalRead(resetButtonPin) == LOW) {
resetPressed = true;
}
}
}
void showWelcome() {
lcd.clear();
lcd.setCursor(0, 0); lcd.print("Library System");
lcd.setCursor(0, 2); lcd.print("1. User Login");
lcd.setCursor(0, 3); lcd.print("2. Admin Login");
Serial.println("\nEnter 1 for User Login or 2 for Admin Login:");
}
void userLogin() {
lcd.clear();
lcd.setCursor(0, 1); lcd.print("Enter User Password:");
Serial.println("Enter User Password:");
String pw = waitForInput();
if (resetPressed) return;
if (pw == userPassword) {
accessOK("User");
userMenu();
} else {
accessDenied();
if (!isLocked) showWelcome();
}
}
void adminLogin() {
lcd.clear();
lcd.setCursor(0, 1); lcd.print("Enter Admin Password:");
Serial.println("Enter Admin Password:");
String pw = waitForInput();
if (resetPressed) return;
if (pw == adminPassword) {
accessOK("Admin");
adminMenu();
} else {
accessDenied();
if (!isLocked) showWelcome();
}
}
// --- MODIFIED: viewSubMenuUser with corrected book list display ---
void viewSubMenuUser() {
lcd.clear();
lcd.setCursor(0,0); lcd.print("1.View Details");
lcd.setCursor(0,1); lcd.print("2.View My Books");
lcd.setCursor(0,2); lcd.print("3.Renew Book");
lcd.setCursor(0,3); lcd.print("4.Issue 5.Return"); // Combined for space
Serial.println("\nEnter option: 1=Details, 2=My Books, 3=Renew, 4=Issue, 5=Return");
String j = waitForInput();
if (resetPressed) return;
if(j == "1") {
char d[50];
readStringFromEEPROM(ADDR_BOOK_DETAILS, d, 50);
lcd.clear(); lcd.print("Details:"); lcd.setCursor(0, 1); lcd.print(d);
Serial.println("Details: " + String(d));
delay(4000);
} else if(j == "2") {
// --- FIX: This block now shows the actual borrowed books ---
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Your Borrowed Books:");
Serial.println("\n--- Your Borrowed Books ---");
int displayCount = 0;
for (int i = 0; i < MAX_USER_BOOKS; i++) {
if (userBorrowedBooks[i] != -1) {
int bookIndex = userBorrowedBooks[i];
String bookName = libraryStock[bookIndex].name;
lcd.setCursor(0, displayCount + 1); // Display on LCD lines 1, 2, 3
lcd.print(String(displayCount + 1) + ". " + bookName);
Serial.println(String(displayCount + 1) + ". " + bookName);
displayCount++;
}
}
if (displayCount == 0) {
lcd.setCursor(0, 1);
lcd.print("You have no books.");
Serial.println("You have no books borrowed.");
}
delay(4000);
} else if (j == "3") {
renewBookMenu("User");
} else if (j == "4") {
issueNewBook();
} else if (j == "5") {
returnExistingBook();
}
}
void userMenu() {
while (true) {
if (resetPressed) return;
lcd.clear();
lcd.setCursor(0, 0); lcd.print("User Menu");
lcd.setCursor(0, 1); lcd.print("1.View Menu");
lcd.setCursor(0, 2); lcd.print("2.Send Msg to Admin");
lcd.setCursor(0, 3); lcd.print("3.Read Msg 4.Back");
Serial.println("\nUser Menu: 1=View, 2=Send Msg, 3=Read Msg, 4=Back");
String o = waitForInput();
if (resetPressed) return;
if (o == "1") { viewSubMenuUser(); }
else if (o == "2") {
lcd.clear();
lcd.setCursor(0, 1); lcd.print("Type message in Serial");
Serial.println("Enter message to send to admin:");
String m = waitForInput();
if(resetPressed) return;
writeStringToEEPROM(ADDR_USER_MSG, m);
EEPROM.commit();
lcd.clear(); lcd.setCursor(7, 1); lcd.print("Sent!");
delay(1500);
} else if (o == "3") {
char r[50];
readStringFromEEPROM(ADDR_ADMIN_MSG, r, 50);
lcd.clear();
lcd.setCursor(0, 0); lcd.print("Admin Msg:");
lcd.setCursor(0, 1); lcd.print(r);
Serial.println("Admin Msg: " + String(r));
delay(4000);
} else if (o == "4") {
showWelcome();
return;
}
}
}
void adminMenu() {
while (true) {
if (resetPressed) return;
lcd.clear();
lcd.setCursor(0, 0); lcd.print("Admin Menu");
lcd.setCursor(0, 1); lcd.print("1.Edit Menu");
lcd.setCursor(0, 2); lcd.print("2.Read User Msg");
lcd.setCursor(0, 3); lcd.print("3.Send Reply 4.Back");
Serial.println("\nAdmin Menu: 1=Edit, 2=Read Msg, 3=Send Reply, 4=Back");
String o = waitForInput();
if (resetPressed) return;
if (o == "1") { editSubMenuAdmin(); }
else if (o == "2") {
char um[50];
readStringFromEEPROM(ADDR_USER_MSG, um, 50);
lcd.clear();
lcd.setCursor(0, 0); lcd.print("User Msg:");
lcd.setCursor(0, 1); lcd.print(um);
Serial.println("User Msg: " + String(um));
delay(4000);
} else if (o == "3") {
lcd.clear();
lcd.setCursor(0, 1); lcd.print("Type reply in Serial");
Serial.println("Enter reply to send to user:");
String r = waitForInput();
if(resetPressed) return;
writeStringToEEPROM(ADDR_ADMIN_MSG, r);
EEPROM.commit();
lcd.clear(); lcd.setCursor(7, 1); lcd.print("Sent!");
delay(1500);
} else if (o == "4") {
showWelcome();
return;
}
}
}
// --- MODIFIED: renewBookMenu to show actual borrowed books ---
void renewBookMenu(String role) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Select book to renew:");
Serial.println("\nSelect a book to renew:");
// --- NEW: Display the user's actual borrowed books ---
int displayCount = 0;
for (int i = 0; i < MAX_USER_BOOKS; i++) {
if (userBorrowedBooks[i] != -1) { // Check if a book is borrowed in this slot
int bookStockIndex = userBorrowedBooks[i];
String bookName = libraryStock[bookStockIndex].name;
lcd.setCursor(0, displayCount + 1);
String displayStr = String(displayCount + 1) + ". " + bookName;
lcd.print(displayStr);
Serial.println(displayStr);
displayCount++;
}
}
// Handle case where user has no books
if (displayCount == 0) {
lcd.setCursor(0, 1);
lcd.print("No books to renew.");
Serial.println("No books to renew.");
delay(2000);
return;
}
String k = waitForInput();
if (resetPressed) return;
int choiceIndex = k.toInt() - 1; // User input '1' becomes index 0
const int renew_time_addrs[] = {ADDR_BOOK1_RENEW_TIME, ADDR_BOOK2_RENEW_TIME, ADDR_BOOK3_RENEW_TIME};
// Check if the choice is valid (e.g., 1 or 2 if they have 2 books)
if (choiceIndex >= 0 && choiceIndex < displayCount) {
// Get the actual name of the chosen book for the confirmation message
int bookStockIndex = userBorrowedBooks[choiceIndex];
String chosenBookName = libraryStock[bookStockIndex].name;
// Renew the timer that corresponds to the chosen book's slot
lastRenewTimes[choiceIndex] = millis();
EEPROM.put(renew_time_addrs[choiceIndex], lastRenewTimes[choiceIndex]);
EEPROM.commit();
lcd.clear();
lcd.setCursor(0, 1);
lcd.print(chosenBookName); // Display the real book name
lcd.setCursor(0, 2);
lcd.print("Renewed! Timer reset");
Serial.println(chosenBookName + " renewed. Timer reset");
} else {
lcd.clear();
lcd.setCursor(2, 1);
lcd.print("Invalid Selection");
}
delay(2500);
}
void accessOK(String role) {
lcd.clear();
lcd.setCursor(3, 1);
lcd.print(role + " Access OK");
digitalWrite(greenLEDPin, HIGH);
delay(1500);
digitalWrite(greenLEDPin, LOW);
}
void systemLocked() {
lcd.clear();
lcd.setCursor(0, 1);
lcd.print("System Locked!");
tone(buzzerPin, 1000, 500);
noTone(buzzerPin);
}
void accessDenied() {
attemptCount++;
lcd.clear();
lcd.setCursor(3, 1);
lcd.print("Access Denied");
digitalWrite(redLEDPin, HIGH);
delay(1000);
digitalWrite(redLEDPin, LOW);
Serial.println("Access Denied");
if (attemptCount >= 3) {
isLocked = true;
lockTime = millis();
systemLocked();
} else {
delay(1000);
}
}
String waitForInput() {
while (!Serial.available()) {
checkReset();
if (resetPressed) return "";
Blynk.run();
delay(50);
}
String input = Serial.readStringUntil('\n');
input.trim();
return input;
}
void renewBookFromBlynk(int bookIndex, const String& eventCode) {
Serial.println("Book renewal for " + bookNames[bookIndex] + " triggered from Blynk. Timer is being reset.");
int renew_time_addrs[] = {ADDR_BOOK1_RENEW_TIME, ADDR_BOOK2_RENEW_TIME, ADDR_BOOK3_RENEW_TIME};
lastRenewTimes[bookIndex] = millis();
EEPROM.put(renew_time_addrs[bookIndex], lastRenewTimes[bookIndex]);
EEPROM.commit();
Blynk.logEvent(eventCode);
}
BLYNK_WRITE(V0) {
if (param.asInt() == 1) {
renewBookFromBlynk(0, "renew_book1_ok");
Blynk.virtualWrite(V0, 0);
}
}
BLYNK_WRITE(V1) {
if (param.asInt() == 1) {
renewBookFromBlynk(1, "renew_book2_ok");
Blynk.virtualWrite(V1, 0);
}
}
BLYNK_WRITE(V2) {
if (param.asInt() == 1) {
renewBookFromBlynk(2, "renew_book2_ok");
Blynk.virtualWrite(V2, 0);
}
}