#include <SPI.h>
#include <MFRC522.h>
#include <WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
// --- Pin Definitions ---
#define SS_PIN 5
#define RST_PIN 22
#define GREEN_LED 25
#define RED_LED 26
#define TRIG_PIN 14
#define ECHO_PIN 12
// --- Network & Shared MQTT Broker Configuration ---
const char* SSID = "Wokwi-GUEST";
const char* PASSWORD = "";
const char* MQTT_BROKER = "broker.hivemq.com";
const int MQTT_PORT = 1883;
// Global topic for all school terminals to clear active pass timers
const char* TOPIC_PASS_EVENTS = "school/hallway/pass/events";
WiFiClient espClient;
PubSubClient mqttClient(espClient);
MFRC522 rfid(SS_PIN, RST_PIN);
// --- Recognized Card UIDs ---
const String FLOOR_INCHARGE_UID = "55667788";
const String TEACHER_CARD_UID = "1234";
const String STUDENT_1_UID = "11223344";
// --- State Variables ---
bool teacherPresent = false;
unsigned long exitTimestamp = 0;
bool studentOutUnscanned = false;
bool alertActive = false;
String activeStudentUID = "";
const int DOORWAY_TRIGGER_CM = 30;
long lastDistance = 9999;
const unsigned long GRACE_PERIOD_TEACHER_ABSENT = 7UL * 60UL * 1000UL; // 7 minutes
const unsigned long ALERT_WINDOW_TEACHER_PRESENT = 2UL * 60UL * 1000UL; // 2 minutes
// --- Network Initialization ---
void connectWiFi() {
Serial.print("Connecting to Wi-Fi...");
WiFi.begin(SSID, PASSWORD);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println(" Connected!");
}
void connectMQTT() {
while (!mqttClient.connected()) {
Serial.print("Connecting to MQTT...");
String clientId = "ESP32-Room101-" + String(random(0xffff), HEX);
if (mqttClient.connect(clientId.c_str())) {
Serial.println(" Connected!");
// Subscribe to pass events so destination taps (Infirmary/Reception) auto-clear local timer
mqttClient.subscribe(TOPIC_PASS_EVENTS);
} else {
Serial.print(" Failed, rc=");
Serial.print(mqttClient.state());
delay(2000);
}
}
}
// --- Multi-Terminal Incoming MQTT Listener ---
void mqttCallback(char* topic, byte* payload, unsigned int length) {
StaticJsonDocument<384> doc;
DeserializationError error = deserializeJson(doc, payload, length);
if (error) {
Serial.print("MQTT JSON parse error: ");
Serial.println(error.f_str());
return;
}
const char* eventType = doc["eventType"];
const char* studentUID = doc["studentId"];
const char* terminal = doc["terminal"];
// If student tapped at another terminal (Infirmary/Reception), auto-clear local countdown
if (studentOutUnscanned && String(studentUID) == activeStudentUID) {
if (String(eventType) == "ARRIVED") {
Serial.print("Pass cleared! Student ");
Serial.print(studentUID);
Serial.print(" checked in at destination: ");
Serial.println(terminal);
studentOutUnscanned = false;
alertActive = false;
activeStudentUID = "";
exitTimestamp = 0;
}
}
}
// --- Helper Function to Publish JSON Payload ---
void publishEvent(String eventType, String detail, String studentId = "") {
if (!mqttClient.connected()) connectMQTT();
StaticJsonDocument<384> doc;
doc["room"] = "ROOM 101";
doc["terminal"] = "ROOM_101";
doc["teacherPresent"] = teacherPresent;
doc["studentOut"] = studentOutUnscanned;
doc["alertActive"] = alertActive;
doc["eventType"] = eventType;
doc["detail"] = detail;
doc["studentId"] = studentId.length() > 0 ? studentId : activeStudentUID;
char buffer[384];
serializeJson(doc, buffer);
mqttClient.publish(TOPIC_PASS_EVENTS, buffer);
Serial.print("Published MQTT: ");
Serial.println(buffer);
}
// --- Sensor & RFID Logic ---
long readDistanceCM() {
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
long duration = pulseIn(ECHO_PIN, HIGH, 30000);
if (duration == 0) return 9999;
return duration * 0.0343 / 2;
}
void checkCardTap() {
if (!rfid.PICC_IsNewCardPresent() || !rfid.PICC_ReadCardSerial()) {
return;
}
String cardUID = "";
for (byte i = 0; i < rfid.uid.size; i++) {
cardUID += String(rfid.uid.uidByte[i], HEX);
}
cardUID.toUpperCase();
if (cardUID == FLOOR_INCHARGE_UID) {
if (studentOutUnscanned) {
studentOutUnscanned = false;
alertActive = false;
activeStudentUID = "";
publishEvent("ADMIN_OVERRIDE", "Floor Incharge cleared active room alert.", cardUID);
}
}
else if (cardUID == TEACHER_CARD_UID) {
teacherPresent = !teacherPresent;
digitalWrite(RED_LED, teacherPresent ? LOW : HIGH);
digitalWrite(GREEN_LED, teacherPresent ? HIGH : LOW);
publishEvent("TEACHER_TOGGLE", teacherPresent ? "ACTIVE CLASS" : "CLASS ENDED", cardUID);
}
else if (cardUID == STUDENT_1_UID) {
if (teacherPresent) {
activeStudentUID = cardUID;
publishEvent("STUDENT_PASS", "Pass Approved for UID: " + cardUID, cardUID);
} else {
publishEvent("DENIED", "Room locked. Teacher must unlock room first.", cardUID);
}
}
else {
publishEvent("DENIED", "Unregistered Card UID: " + cardUID, cardUID);
}
rfid.PICC_HaltA();
rfid.PCD_StopCrypto1();
}
void checkDoorwaySensor() {
long distance = readDistanceCM();
bool passageDetected = (distance < DOORWAY_TRIGGER_CM && lastDistance >= DOORWAY_TRIGGER_CM);
lastDistance = distance;
if (!passageDetected) return;
if (!teacherPresent && !studentOutUnscanned) {
studentOutUnscanned = true;
exitTimestamp = millis();
if (activeStudentUID == "") activeStudentUID = STUDENT_1_UID; // Default active student context
publishEvent("UNSCANNED_EXIT", "Teacher Absent - 7-min grace started");
}
else if (!teacherPresent && studentOutUnscanned) {
studentOutUnscanned = false;
alertActive = false;
publishEvent("RE_ENTRY", "Student re-entry detected. Grace timer cleared.");
activeStudentUID = "";
}
else if (teacherPresent) {
studentOutUnscanned = true;
exitTimestamp = millis();
if (activeStudentUID == "") activeStudentUID = STUDENT_1_UID;
publishEvent("UNSCANNED_EXIT", "Active Class - 2-min alert timer started");
}
}
void checkGraceTimerExpiry() {
if (!studentOutUnscanned || alertActive) return;
unsigned long elapsed = millis() - exitTimestamp;
unsigned long limit = teacherPresent ? ALERT_WINDOW_TEACHER_PRESENT : GRACE_PERIOD_TEACHER_ABSENT;
if (elapsed >= limit) {
alertActive = true;
publishEvent("ALERT_TIMEOUT", "Unaccounted exit timed out!");
}
}
// --- Arduino Core Routines ---
void setup() {
Serial.begin(115200);
SPI.begin();
rfid.PCD_Init();
pinMode(GREEN_LED, OUTPUT);
pinMode(RED_LED, OUTPUT);
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
digitalWrite(RED_LED, HIGH);
digitalWrite(GREEN_LED, LOW);
connectWiFi();
mqttClient.setServer(MQTT_BROKER, MQTT_PORT);
mqttClient.setCallback(mqttCallback);
}
void loop() {
if (!mqttClient.connected()) connectMQTT();
mqttClient.loop();
checkCardTap();
checkDoorwaySensor();
checkGraceTimerExpiry();
delay(50);
}Loading
mfrc522
mfrc522