#include <LiquidCrystal_I2C.h>
#include <Servo.h>
#include <IRremote.h>
#include <DHT.h>
#include <Keypad.h>
#include "RTClib.h"
#include "SafeState.h"
#define MENU_I2C_ADDR 0x27
#define MEETING_I2C_ADDR 0x26
#define DORM_I2C_ADDR 0x25
#define CLASS_I2C_ADDR 0x24
#define DORM_HALLWAY_I2C_ADDR 0x23
#define LCD_COLUMNS 20
#define LCD_LINES 4
#define MEETING_DHT_PIN 22
#define MEETING_LED 23
#define DORM_DHT_PIN 24
#define DOOR_PIN 25
#define DORM_LED 26
#define CLASS_DHT_PIN 27
#define CLASS_LED 28
#define DORM_HALLWAY_DHT_PIN 29
#define DORM_HALLWAY_LED 30
#define MOTION_PIN 31
#define ECHO_PIN 32
#define TRIG_PIN 33
#define YARD_LED_PIN 34
#define YARD_LIGHT_SENSOR_PIN 35
#define BUZZER 11
#define IR_RECEIVER_PIN 10
#define CLOSE_POS 90
#define OPEN_POS 0
LiquidCrystal_I2C menuLcd(MENU_I2C_ADDR, LCD_COLUMNS, LCD_LINES);
LiquidCrystal_I2C meetingLcd(MEETING_I2C_ADDR, LCD_COLUMNS, LCD_LINES);
LiquidCrystal_I2C dormLcd(DORM_I2C_ADDR, LCD_COLUMNS, LCD_LINES);
LiquidCrystal_I2C classLcd(CLASS_I2C_ADDR, LCD_COLUMNS, LCD_LINES);
LiquidCrystal_I2C dormHallwayLcd(DORM_HALLWAY_I2C_ADDR, LCD_COLUMNS, LCD_LINES);
IRrecv receiver(IR_RECEIVER_PIN);
DHT meetingDht(MEETING_DHT_PIN, DHT22);
DHT dormDht(DORM_DHT_PIN, DHT22);
DHT classDht(CLASS_DHT_PIN, DHT22);
DHT dormHallwayDht(DORM_HALLWAY_DHT_PIN, DHT22);
RTC_DS1307 meetingRTC;
Servo DormRoomServo;
SafeState safeState;
enum ScreenState {
MENU,
MEETING_ROOM,
CLASS_ROOM,
DORM_ROOM,
DORM_HALLWAY,
SCHOOL_YARD
};
char dorm_keys[4][4] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
byte dorm_rowPins[4] = {9, 8, 7, 6};
byte dorm_colPins[4] = {5, 4, 3, 2};
Keypad dorm_keypad = Keypad(makeKeymap(dorm_keys), dorm_rowPins, dorm_colPins, 4, 4);
#pragma region Custom Char
uint8_t pacman[8] = {
0b00000,
0b00000,
0b01110,
0b11011,
0b11111,
0b01110,
0b00000,
0b00000
};
uint8_t pacmanOpen[] = {
0b00000,
0b00000,
0b01110,
0b11011,
0b11100,
0b01110,
0b00000,
0b00000
};
uint8_t dot[] = {
0b00000,
0b00000,
0b00000,
0b00110,
0b00110,
0b00000,
0b00000,
0b00000
};
char daysOfTheWeek[7][12] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
ScreenState currentState = MENU;
#pragma endregion
#pragma region Custom Student ID
struct Student {
String id;
String name;
};
Student students[] = {
{"060406", "Nguyen Minh Duc"},
{"060408", "Tran Luu Dung"},
{"060402", "Nguyen Duc Anh"},
{"060330", "Nguyen Hong Phuoc"},
{"060301", "Bui Quoc An"}
};
int studentCount = 0;
int totalStudents = sizeof(students) / sizeof(Student);
#pragma endregion
bool meetingLedState = false;
bool dormLedState = false;
bool classLedState = false;
bool dormHallwayLedState = false;
bool motionDetected = false;
bool isOnfirstPage = true;
bool schoolYardLedState = false;
bool dormRoomAccessGranted = false;
bool fireDetected = false;
void loadingPacman();
void setupMenuLcd();
void showMenu();
void showNextMenu();
void translateIR();
void handleDht();
void handlemeetingRTC();
void handleStudentID();
void handleMotion();
void lock();
void unlock();
void showUnlockMessage();
void setNewCode();
void unlockSafeLogic();
void updateMeetingRoom();
void updateDormRoom();
void updateClassroom();
void updateDormHallway();
void updateDormHallwayPage2();
void updateSchoolYard();
void updateFireAlert();
String findStudentById(String id);
String inputStudentId();
String inputSecretCode();
float readDistanceCM();
void setup() {
Serial.begin(9600);
meetingDht.begin();
dormDht.begin();
classDht.begin();
dormHallwayDht.begin();
if (! meetingRTC.begin()) {
abort();
}
pinMode(MEETING_LED, OUTPUT);
digitalWrite(MEETING_LED, LOW);
pinMode(DORM_LED, OUTPUT);
digitalWrite(DORM_LED, LOW);
pinMode(CLASS_LED, OUTPUT);
digitalWrite(CLASS_LED, LOW);
pinMode(DORM_HALLWAY_LED, OUTPUT);
digitalWrite(DORM_HALLWAY_LED, LOW);
pinMode(MOTION_PIN, INPUT);
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
pinMode(YARD_LED_PIN, OUTPUT);
digitalWrite(YARD_LED_PIN, LOW);
pinMode(YARD_LIGHT_SENSOR_PIN, INPUT);
pinMode(BUZZER, OUTPUT);
DormRoomServo.attach(DOOR_PIN);
DormRoomServo.write(CLOSE_POS);
safeState.setCode("1234");
setupMenuLcd();
receiver.enableIRIn();
showMenu();
}
void loop() {
updateFireAlert();
if (receiver.decode()) {
translateIR();
receiver.resume();
}
if (currentState == MEETING_ROOM) {
updateMeetingRoom();
}
if (currentState == DORM_ROOM && dormRoomAccessGranted) {
updateDormRoom();
}
if (currentState == CLASS_ROOM) {
updateClassroom();
}
if (currentState == DORM_HALLWAY) {
if (isOnfirstPage) {
updateDormHallway();
} else {
updateDormHallwayPage2();
}
}
if (currentState == SCHOOL_YARD) {
updateSchoolYard();
}
}
#pragma region Menu
void loadingPacman() {
for (int i = 5; i < 15; i++) {
menuLcd.setCursor(i, 3);
menuLcd.print("\1");
for (int j = i + 1; j < 15; j++) {
menuLcd.setCursor(j, 3);
menuLcd.print("\2");
}
menuLcd.createChar(1, pacman);
delay(200);
menuLcd.createChar(1, pacmanOpen);
delay(200);
menuLcd.setCursor(i, 3);
menuLcd.print(" ");
}
}
void setupMenuLcd() {
menuLcd.init();
menuLcd.backlight();
meetingLcd.init();
meetingLcd.backlight();
dormLcd.init();
dormLcd.backlight();
classLcd.init();
classLcd.backlight();
dormHallwayLcd.init();
dormHallwayLcd.backlight();
menuLcd.createChar(1, pacman);
menuLcd.createChar(2, dot);
menuLcd.setCursor(2, 0);
menuLcd.print("Nguyen Minh Duc_");
menuLcd.setCursor(6, 1);
menuLcd.print("CT060406");
menuLcd.setCursor(0, 2);
menuLcd.print("Smart School System!");
// loadingPacman();
menuLcd.clear();
}
void showMenu() {
menuLcd.clear();
meetingLcd.clear();
dormLcd.clear();
classLcd.clear();
dormHallwayLcd.clear();
menuLcd.setCursor(0, 0);
menuLcd.print("Menu:");
menuLcd.setCursor(0, 1);
menuLcd.print("1. Meeting room");
menuLcd.setCursor(0, 2);
menuLcd.print("2. Class room");
menuLcd.setCursor(0, 3);
menuLcd.print("3. Dormitory room");
}
void showNextMenu() {
menuLcd.clear();
menuLcd.setCursor(0, 0);
menuLcd.print("Menu:");
menuLcd.setCursor(0, 1);
menuLcd.print("4. Dormitory hallway");
menuLcd.setCursor(0, 2);
menuLcd.print("5. School yard");
menuLcd.setCursor(0, 3);
menuLcd.print("<- Back");
}
#pragma endregion
void translateIR() {
switch (receiver.decodedIRData.command) {
case 162: // Power button
break;
case 226:
currentState = MENU;
dormRoomAccessGranted = false;
showMenu();
break;
case 144:
if (currentState == MENU) {
showNextMenu();
}
if (currentState == DORM_HALLWAY) {
isOnfirstPage = false;
dormHallwayLcd.clear();
updateDormHallwayPage2();
}
break;
case 224:
if (currentState == MENU) {
showMenu();
}
if (currentState == DORM_HALLWAY) {
isOnfirstPage = true;
dormHallwayLcd.clear();
updateDormHallway();
}
break;
case 168:
if (currentState == MEETING_ROOM) {
meetingLedState = !meetingLedState;
digitalWrite(MEETING_LED, meetingLedState ? HIGH : LOW);
}
if (currentState == DORM_ROOM && dormRoomAccessGranted) {
dormLedState = !dormLedState;
digitalWrite(DORM_LED, dormLedState ? HIGH : LOW);
}
if (currentState == CLASS_ROOM) {
classLedState = !classLedState;
digitalWrite(CLASS_LED, classLedState ? HIGH : LOW);
}
break;
case 48:
currentState = MEETING_ROOM;
menuLcd.clear();
menuLcd.setCursor(3, 1);
menuLcd.print("Meeting room..");
break;
case 24:
currentState = CLASS_ROOM;
menuLcd.clear();
menuLcd.setCursor(4, 1);
menuLcd.print("Class room..");
break;
case 16:
currentState = DORM_HALLWAY;
menuLcd.clear();
menuLcd.setCursor(3, 1);
menuLcd.print("Dorm hallway..");
break;
case 56:
currentState = SCHOOL_YARD;
menuLcd.clear();
menuLcd.setCursor(3, 0);
menuLcd.print("School yard...");
break;
case 122:
menuLcd.clear();
menuLcd.setCursor(2, 1);
menuLcd.print("Dormitory Room..");
unlockSafeLogic();
if (dormRoomAccessGranted) {
currentState = DORM_ROOM;
dormLcd.clear();
dormLcd.setCursor(3, 0);
dormLcd.print("Access Granted");
}
break;
}
}
void updateFireAlert() {
float temp1 = meetingDht.readTemperature();
float temp2 = dormDht.readTemperature();
float temp3 = classDht.readTemperature();
float temp4 = dormHallwayDht.readTemperature();
while (temp1 > 70 || temp2 > 70 || temp3 > 70 || temp4 > 70) {
if (!fireDetected) {
menuLcd.clear();
meetingLcd.clear();
dormLcd.clear();
classLcd.clear();
dormHallwayLcd.clear();
menuLcd.setCursor(3, 1);
menuLcd.print("Fire Detected");
menuLcd.setCursor(5, 2);
menuLcd.print("!! RUNN !!");
meetingLcd.setCursor(3, 1);
meetingLcd.print("Fire Detected");
meetingLcd.setCursor(5, 2);
meetingLcd.print("!! RUNN !!");
dormLcd.setCursor(3, 1);
dormLcd.print("Fire Detected");
dormLcd.setCursor(5, 2);
dormLcd.print("!! RUNN !!");
classLcd.setCursor(3, 1);
classLcd.print("Fire Detected");
classLcd.setCursor(5, 2);
classLcd.print("!! RUNN !!");
dormHallwayLcd.setCursor(3, 1);
dormHallwayLcd.print("Fire Detected");
dormHallwayLcd.setCursor(5, 2);
dormHallwayLcd.print("!! RUNN !!");
fireDetected = true;
}
tone(BUZZER, 262);
temp1 = meetingDht.readTemperature();
temp2 = dormDht.readTemperature();
temp3 = classDht.readTemperature();
temp4 = dormHallwayDht.readTemperature();
}
if (fireDetected) {
fireDetected = false;
noTone(BUZZER);
showMenu();
}
}
#pragma region Meeting room
void handleDht() {
float humidity = meetingDht.readHumidity();
float temperature = meetingDht.readTemperature();
meetingLcd.setCursor(0, 1);
meetingLcd.print("Humid: ");
meetingLcd.print(humidity);
meetingLcd.print("%");
meetingLcd.setCursor(0, 2);
meetingLcd.print("Temp: ");
meetingLcd.print(temperature);
meetingLcd.print(" C");
}
void handlemeetingRTC() {
DateTime now = meetingRTC.now();
meetingLcd.setCursor(0, 0);
meetingLcd.print(daysOfTheWeek[now.dayOfTheWeek()]);
meetingLcd.print(" ");
meetingLcd.print(now.day(), DEC);
meetingLcd.print('/');
meetingLcd.print(now.month(), DEC);
meetingLcd.print(" ");
meetingLcd.print(now.hour(), DEC);
meetingLcd.print(':');
meetingLcd.print(now.minute(), DEC);
meetingLcd.print(':');
meetingLcd.print(now.second(), DEC);
}
void updateMeetingRoom() {
handleDht();
handlemeetingRTC();
meetingLcd.setCursor(0, 3);
meetingLcd.print("LED: ");
meetingLcd.print(meetingLedState ? "ON " : "OFF");
}
#pragma endregion
#pragma region Dorm room
void lock() {
DormRoomServo.write(CLOSE_POS);
safeState.lock();
}
void unlock() {
DormRoomServo.write(OPEN_POS);
}
void showUnlockMessage() {
dormLcd.clear();
dormLcd.setCursor(3, 1);
dormLcd.print("Door Unlocked!");
delay(1000);
}
void setNewCode() {
dormLcd.clear();
dormLcd.setCursor(3, 0);
dormLcd.print("Enter new code");
String newCode = inputSecretCode();
dormLcd.clear();
dormLcd.setCursor(2, 0);
dormLcd.print("Confirm new code");
String confirmCode = inputSecretCode();
if (newCode.equals(confirmCode)) {
safeState.setCode(newCode);
dormLcd.clear();
dormLcd.setCursor(4, 1);
dormLcd.print("Code updated");
delay(2000);
} else {
dormRoomAccessGranted = false;
dormLcd.clear();
dormLcd.setCursor(3, 1);
dormLcd.print("Code mismatch!");
delay(2000);
dormLcd.clear();
currentState = MENU;
showMenu();
}
}
void unlockSafeLogic() {
dormLcd.clear();
dormLcd.setCursor(0, 0);
dormLcd.print("Enter code:");
String userCode = inputSecretCode();
bool unlockedSuccessfully = safeState.unlock(userCode);
if (unlockedSuccessfully) {
showUnlockMessage();
unlock();
dormRoomAccessGranted = true;
dormLcd.clear();
dormLcd.setCursor(0, 0);
dormLcd.print("A: Set new code");
dormLcd.setCursor(0, 1);
dormLcd.print("B: See information");
char key = 0;
while (key != 'A' && key != 'B') {
key = dorm_keypad.getKey();
}
if (key == 'A') {
setNewCode();
}
lock();
} else {
dormRoomAccessGranted = false;
dormLcd.clear();
dormLcd.setCursor(0, 0);
dormLcd.print("Access Denied!");
delay(2000);
dormLcd.clear();
currentState = MENU;
showMenu();
}
}
String inputSecretCode() {
dormLcd.setCursor(7, 1);
dormLcd.print("[____]");
dormLcd.setCursor(8, 1);
String result = "";
while (result.length() < 4) {
char key = dorm_keypad.getKey();
if (key >= '0' && key <= '9') {
dormLcd.print('*');
result += key;
}
}
return result;
}
void updateDormRoom() {
if (!dormRoomAccessGranted) {
dormLcd.clear();
dormLcd.setCursor(0, 0);
dormLcd.print("Access Locked!");
currentState = MENU;
showMenu();
return;
}
float humidity = dormDht.readHumidity();
float temperature = dormDht.readTemperature();
dormLcd.setCursor(0, 1);
dormLcd.print("Humid: ");
dormLcd.print(humidity);
dormLcd.print("%");
dormLcd.setCursor(0, 2);
dormLcd.print("Temp: ");
dormLcd.print(temperature);
dormLcd.print(" C");
dormLcd.setCursor(0, 3);
dormLcd.print("LED: ");
dormLcd.print(dormLedState ? "ON " : "OFF");
}
#pragma endregion
#pragma region Classroom
String findStudentById(String inputId) {
for (int i = 0; i < totalStudents; i++) {
if (students[i].id.equals(inputId)) {
return students[i].name;
}
}
return "Unknown Student";
}
void updateClassroom() {
classLcd.setCursor(0, 0);
classLcd.print("Students: ");
classLcd.print(studentCount);
classLcd.print("/");
classLcd.print(totalStudents);
float humidity = classDht.readHumidity();
float temperature = classDht.readTemperature();
classLcd.setCursor(0, 1);
classLcd.print("Humid: ");
classLcd.print(humidity);
classLcd.print("%");
classLcd.setCursor(0, 2);
classLcd.print("Temp: ");
classLcd.print(temperature);
classLcd.print(" C");
classLcd.setCursor(0, 3);
classLcd.print("LED: ");
classLcd.print(classLedState ? "ON " : "OFF");
char key = dorm_keypad.getKey();
if (key == '*' && studentCount < totalStudents) {
handleStudentID();
}
}
void handleStudentID() {
classLcd.clear();
classLcd.setCursor(0, 0);
classLcd.print("Enter Student ID:");
String enteredID = "";
int IdLength = 0;
classLcd.setCursor(6, 1);
classLcd.print("[______]");
classLcd.setCursor(7, 1);
while (IdLength < 6) {
char key = dorm_keypad.getKey();
if (key != NO_KEY && key >= '0' && key <= '9') {
classLcd.print(key);
enteredID += key;
IdLength++;
}
}
classLcd.setCursor(0, 2);
classLcd.print("Press # to Check");
while (true) {
char key = dorm_keypad.getKey();
if (key == '#') {
String name = findStudentById(enteredID);
if (!name.equals("Unknown Student")) {
classLcd.clear();
classLcd.setCursor(0, 0);
classLcd.print("Welcome ");
classLcd.setCursor(0, 1);
classLcd.print(name);
classLcd.setCursor(0, 2);
classLcd.print(enteredID);
studentCount++;
delay(2000);
classLcd.clear();
break;
} else {
classLcd.clear();
classLcd.setCursor(2, 1);
classLcd.print(name);
delay(2000);
classLcd.clear();
break;
}
}
}
}
#pragma endregion
#pragma region Dorm Hallway
void updateDormHallway() {
float humidity = dormHallwayDht.readHumidity();
float temperature = dormHallwayDht.readTemperature();
dormHallwayLcd.setCursor(0, 0);
dormHallwayLcd.print("Humid: ");
dormHallwayLcd.print(humidity);
dormHallwayLcd.print("%");
dormHallwayLcd.setCursor(0, 1);
dormHallwayLcd.print("Temp: ");
dormHallwayLcd.print(temperature);
dormHallwayLcd.print(" C");
handleMotion();
}
void handleMotion() {
int val = digitalRead(MOTION_PIN);
motionDetected = val == HIGH;
dormHallwayLedState = val == HIGH;
digitalWrite(DORM_HALLWAY_LED, dormHallwayLedState);
dormHallwayLcd.setCursor(0, 2);
dormHallwayLcd.print("Motion: ");
dormHallwayLcd.print(motionDetected ? "Detected " : "No motion");
dormHallwayLcd.setCursor(0, 3);
dormHallwayLcd.print("LED: ");
dormHallwayLcd.print(dormHallwayLedState ? "ON " : "OFF");
}
float readDistanceCM() {
pinMode(TRIG_PIN, OUTPUT);
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
pinMode(ECHO_PIN, INPUT);
return 0.04 / 2 * pulseIn(ECHO_PIN, HIGH);
}
void updateDormHallwayPage2() {
float distance = readDistanceCM();
bool pumpOut = distance < 100;
bool pumpIn = distance > 300;
dormHallwayLcd.setCursor(0, 0);
dormHallwayLcd.print("Distance: ");
dormHallwayLcd.print(distance);
dormHallwayLcd.setCursor(0, 1);
if (pumpOut) {
dormHallwayLcd.print("Action: Pump out ");
} else if (pumpIn) {
dormHallwayLcd.print("Action: Pump in ");
} else {
dormHallwayLcd.print("Action: No action");
}
dormHallwayLcd.setCursor(0, 2);
dormHallwayLcd.print("Pump: ");
dormHallwayLcd.print((pumpOut || pumpIn) ? "Active" : "Inactive");
}
#pragma endregion
#pragma region School Yard
void updateSchoolYard() {
schoolYardLedState = digitalRead(YARD_LIGHT_SENSOR_PIN) == HIGH; // Low = Day | HIGH = Night
digitalWrite(YARD_LED_PIN, schoolYardLedState);
menuLcd.setCursor(0, 1);
menuLcd.print("Outside: ");
menuLcd.print(schoolYardLedState ? "Dark " : "Light");
menuLcd.setCursor(0, 2);
menuLcd.print("LED: ");
menuLcd.print(schoolYardLedState ? "ON " : "OFF");
}
#pragma endregion