Community project
Smart Classroom Safety System

This smart classroom safety system monitors environmental conditions and detects potential hazards using an Arduino Uno and a suite of sensors. It tracks temperature and humidity with the DHT11, detects sound levels with the KY-038 microphone, monitors water presence, measures motion with the MPU-6050 accelerometer, and uses RFID cards for attendance tracking. Visual and audio alerts via LEDs and a buzzer respond to unsafe conditions, while a 4-digit display shows real-time data and class duration countdown.
This guide provides a complete wiring diagram, parts list, and step-by-step assembly instructions for connecting all sensors to the Arduino breadboard. You'll also get the full firmware code that implements environmental monitoring, motion detection for seismic events, noise level tracking, water leak detection, and RFID-based access control—everything needed to deploy a functional safety monitoring system in an educational setting.
Wiring diagram
Interactive · read-only
Pan and zoom to explore the wiring. Remix the project to edit it in your own workspace.
Parts list
Bill of materials| Component | Qty | Notes |
|---|---|---|
| DHT11 | 1 | Digital temperature and humidity sensor (lower accuracy than DHT22) |
| Ky 038 Sound Sensor | 1 | KY-038 sound-detection module: electret microphone + LM393 comparator. Analog raw + digital threshold output (potentiometer-tuned). Common ringer / clap detector; not suitable for audio capture. |
| Water Level Sensor | 1 | An analog water level sensor module that outputs a voltage proportional to the water level. It uses ten interleaved copper traces and an S8050 NPN transistor to convert water conductivity into an analog voltage signal. The higher the water level, the higher the output voltage. Operates on 3.3V to 5V. To extend sensor lifespan, it is recommended to power the sensor only during readings by controlling the VCC pin via a digital output pin rather than connecting it permanently to 5V. |
| Buzzer | 1 | Piezo buzzer for sound output |
| LEDRed | 1 | Standard 3mm/5mm through-hole LED. A current-limiting series resistor is added automatically. |
| Resistor220 Ω | 1 | Through-hole resistor (current-limiting in series with an LED) |
| TM1637 4-Digit 7-Segment Display Module | 1 | 4-digit 7-segment LED display module driven by the TM1637 controller IC. Uses a 2-wire serial interface (CLK + DIO). Available in 0.36 inch and 0.56 inch digit sizes. Supports 8 brightness duty settings. Common modules expose VCC, GND, CLK, and DIO. |
| MFRC522 RFID Module | 1 | 13.56 MHz RFID reader/writer module based on the NXP MFRC522 IC. Communicates over SPI and is commonly sold as an RC522 breakout with an onboard antenna. |
| DFRobot SEN0142 Fermion MPU-6050 6 DOF Sensor Breakout | 1 | DFRobot SEN0142 MPU-6050 breakout with 3-5 V board input, I2C interface, onboard I2C pull-ups, and i2cdevlib Arduino example coverage. |
| LEDGreen | 1 | Green LED — turns on when temperature is too hot (fan/heat indicator) |
| Resistor220 Ω | 1 | 220 ohm current-limiting resistor for green fan LED |
Assembly
12 stepsSet up your breadboard and power rails
Place your 830-point breadboard on the desk. Connect the Arduino's 5V pin to the breadboard's + power rail and GND to the − rail using jumper wires. Also run a short wire from the Arduino's 3.3V pin to a separate row you'll use for the RFID module.
- Tip: Use red wire for power (+) and black wire for ground (−) to keep things organised.
Wire the DHT11 temperature & humidity sensor
Place the DHT11 on the breadboard. Connect VCC → 5V rail, GND → GND rail, DATA → Arduino D7. Add a 10 kΩ pull-up resistor between DATA and VCC (included in the kit).
- Tip: The flat face of the DHT11 faces you. Pin order left-to-right: VCC, DATA, NC, GND.
Wire the Green Fan/Heat LED
Place the green LED on the breadboard. Connect the ANODE (long leg) through a 220 Ω resistor to Arduino D6. Connect the CATHODE (short leg) to the GND rail. This LED lights up when the classroom is too hot.
- ⚠ Always use the 220 Ω resistor in series — never connect an LED directly to a GPIO.
Wire the Red Do Not Disturb LED
Place the red LED on the breadboard. Connect the ANODE (long leg) through a 220 Ω resistor to Arduino D4. Connect the CATHODE (short leg) to the GND rail. This LED lights up when the teacher enables Do Not Disturb by scanning their RFID card.
- ⚠ Always use the 220 Ω resistor in series.
Wire the Buzzer
Place the active buzzer on the breadboard. Connect SIGNAL (+ pin, usually marked) → Arduino D5. Connect GND → GND rail. The buzzer sounds for flood alerts, earthquake alerts, and class dismissal.
- Tip: The active buzzer has a built-in oscillator — it beeps with just HIGH/LOW; no tone() needed.
Wire the KY-038 Sound Sensor
Place the KY-038 on the breadboard. Connect VCC → 5V rail, GND → GND rail, DO (digital output) → Arduino A3 (used as digital pin 17). Turn the small blue potentiometer on the module to set the noise trigger level — clockwise = more sensitive.
- Tip: Use the DO pin only. The AO (analog out) pin is left unconnected for this project.
Wire the Water Level Sensor
Place the water level sensor module near the edge of the breadboard. Connect + → Arduino D12 (pulsed power), − → GND rail, S (signal) → Arduino A0. The sensor is powered only when reading to reduce electrode corrosion.
- Tip: Keep the sensor probe flat and at the depth you want to detect flooding.
- ⚠ Do not leave the sensor powered continuously — it corrodes. The firmware handles pulsed power automatically.
Wire the TM1637 4-Digit 7-Segment Display
Place the TM1637 module on the breadboard. Connect VCC → 5V rail, GND → GND rail, CLK → Arduino D8, DIO → Arduino D9. This display shows the 45-minute class countdown in MM:SS format.
- Tip: The TM1637 module only needs 4 wires total — very simple to connect.
Wire the MFRC522 RFID Module
Place the RFID module on the breadboard. Connect: VCC → 3.3V rail (important!), GND → GND rail, SCK → D13, MOSI → D11, MISO → D12, SDA (SS) → D10, RST → D11. Use the shorter jumper wires for the SPI lines to keep connections neat.
- Tip: The blue Elegoo MFRC522 breakout tolerates 5V SPI signal lines — only the VCC pin needs 3.3V.
- ⚠ The RFID module MUST be powered from 3.3V, NOT 5V — using 5V will damage it permanently.
- ⚠ Double-check every SPI connection before powering on.
Wire the MPU-6050 Gyroscope/Accelerometer
Place the MPU-6050 module on the breadboard. Connect VIN → 5V rail, GND → GND rail, SDA → Arduino A4, SCL → Arduino A5. The MPU-6050 shares the I2C bus — if you have the DHT11 pull-up resistors already in place, no extra pull-ups are needed.
- Tip: Mount the MPU-6050 flat and level on the desk — it detects sharp vibrations as earthquake events.
- Tip: A4 and A5 on the UNO are the dedicated hardware I2C pins.
Connect the Arduino to your computer via USB
Plug the included USB cable from the Elegoo UNO R3 into your computer. The power LED on the Arduino should light up. Double-check all connections before clicking Deploy in Schematik.
- Tip: Go through each component one more time to confirm VCC/GND polarity before powering on.
Enroll your RFID cards
After deploying the firmware, open the Serial Monitor in Schematik (9600 baud). Scan each card or keyfob near the RFID module. The Serial Monitor will print 'UNKNOWN CARD UID: XX,XX,XX,XX'. Copy those hex bytes into the ENROLLED[] array in the firmware code, replacing the placeholder entries. Re-deploy to register the cards.
- Tip: The first card you enroll as 'Teacher' will toggle the Do Not Disturb LED when scanned.
- Tip: You can enroll as many students as you like — just add more rows to the ENROLLED[] array.
Pin assignments
Board wiring reference| Pin | Connection | Type |
|---|---|---|
| 5V | dht11 VCC | power |
| GND | dht11 GND | ground |
| GPIO 7 | dht11 DATA | data |
| GPIO 5 | buzzer SIGNAL | digital |
| GND | buzzer GND | ground |
| GPIO 4 | led_resistor P1 | digital |
| EXT | dnd_led ANODE → Resistor P2 | digital |
| GND | dnd_led GND | ground |
| GPIO 6 | fan_led_resistor P1 | digital |
| EXT | fan_led ANODE → Resistor P2 | digital |
| GND | fan_led GND | ground |
| 5V | sound_sensor VCC | power |
| GND | sound_sensor GND | ground |
| GPIO 17 | sound_sensor DO | data |
| GPIO 12 | water_sensor + | digital |
| GND | water_sensor - | ground |
| GPIO 18 | water_sensor S | analog |
| 5V | seg7 VCC | power |
| GND | seg7 GND | ground |
| GPIO 8 | seg7 CLK | digital |
| GPIO 9 | seg7 DIO | digital |
| 3V3 | rfid VCC | power |
| GND | rfid GND | ground |
| GPIO 15 | rfid SCK | spi |
| GPIO 16 | rfid MOSI | spi |
| GPIO 14 | rfid MISO | spi |
| GPIO 10 | rfid SDA | spi |
| GPIO 11 | rfid RST | digital |
| 5V | mpu6050 VIN | power |
| GND | mpu6050 GND | ground |
| GPIO 2 | mpu6050 SDA | i2c |
| GPIO 3 | mpu6050 SCL | i2c |
Firmware
Arduino#include <Arduino.h>
#include <Wire.h>
#include <DHT.h>
#include <TM1637Display.h>
#include <SPI.h>
#include <MFRC522.h>
#include <MPU6050.h>
// ── Pin Definitions ───────────────────────────────────────────
#define DHT_PIN 7
#define FAN_LED_PIN 6 // Green LED — lights when temp >= threshold
#define BUZZER_PIN 5
#define DND_LED_PIN 4
#define SOUND_DO_PIN 17 // A3 used as digital input
#define WATER_POWER_PIN 12 // Pulse power to reduce electrode corrosion
#define WATER_S_PIN A0 // Analog water level signal
#define SEG_CLK_PIN 8
#define SEG_DIO_PIN 9
#define RFID_SS_PIN 10
#define RFID_RST_PIN 11
// ── Thresholds ────────────────────────────────────────────────
#define TEMP_HOT_C 30.0f
#define HUMID_FLOOD_PCT 80.0f
#define WATER_LEVEL_THRESH 400
#define NOISE_DURATION_MS 3000UL
#define QUAKE_THRESHOLD 15000L // raw accel magnitude above 1g baseline
#define CLASS_DURATION_SECS (45L * 60L)
// ── Enrolled RFID UIDs ────────────────────────────────────────
// Scan an unknown card → Serial prints its UID → paste bytes here.
struct RfidCard {
byte uid[4];
const char* name;
};
// Forward declarations
int readWater();
void showCountdown(long secs);
void showSegDashes();
const char* lookupUid(MFRC522::Uid &u);
static const RfidCard ENROLLED[] = {
{ {0xDE, 0xAD, 0xBE, 0xEF}, "Teacher" },
{ {0x11, 0x22, 0x33, 0x44}, "Student 1" },
{ {0x55, 0x66, 0x77, 0x88}, "Student 2" },
};
static const uint8_t ENROLLED_COUNT = sizeof(ENROLLED) / sizeof(ENROLLED[0]);
// ── Objects ───────────────────────────────────────────────────
DHT dht(DHT_PIN, DHT11);
TM1637Display seg(SEG_CLK_PIN, SEG_DIO_PIN);
MFRC522 rfid(RFID_SS_PIN, RFID_RST_PIN);
MPU6050 mpu;
// ── State ─────────────────────────────────────────────────────
float lastT = 25.0f, lastH = 50.0f;
int waterLevel = 0;
bool noiseActive = false;
bool dndActive = false;
unsigned long noiseSince = 0;
unsigned long lastSensor = 0;
unsigned long lastWater = 0;
// Timer
bool timerRunning = false;
long countdownSecs = 0;
unsigned long timerLastTick = 0;
// Earthquake
bool quakeAlert = false;
unsigned long quakeTime = 0;
// Attendance
int attendanceCount = 0;
// ── Helpers ───────────────────────────────────────────────────
int readWater() {
digitalWrite(WATER_POWER_PIN, HIGH);
delayMicroseconds(500);
int v = analogRead(WATER_S_PIN);
digitalWrite(WATER_POWER_PIN, LOW);
return v;
}
void showCountdown(long secs) {
if (secs < 0) secs = 0;
int m = (int)(secs / 60);
int s = (int)(secs % 60);
seg.showNumberDecEx(m * 100 + s, 0x40, true);
}
void showSegDashes() {
uint8_t d[4] = {0x40, 0x40, 0x40, 0x40};
seg.setSegments(d);
}
const char* lookupUid(MFRC522::Uid &u) {
if (u.size != 4) return nullptr;
for (uint8_t i = 0; i < ENROLLED_COUNT; i++) {
if (memcmp(u.uidByte, ENROLLED[i].uid, 4) == 0)
return ENROLLED[i].name;
}
return nullptr;
}
// ── Setup ─────────────────────────────────────────────────────
void setup() {
Serial.begin(9600);
pinMode(FAN_LED_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(DND_LED_PIN, OUTPUT);
pinMode(WATER_POWER_PIN, OUTPUT);
pinMode(SOUND_DO_PIN, INPUT);
digitalWrite(FAN_LED_PIN, LOW);
digitalWrite(BUZZER_PIN, LOW);
digitalWrite(DND_LED_PIN, LOW);
digitalWrite(WATER_POWER_PIN, LOW);
dht.begin();
seg.setBrightness(5);
showSegDashes();
Wire.begin();
SPI.begin();
rfid.PCD_Init();
mpu.initialize();
if (!mpu.testConnection()) {
Serial.println(F("MPU6050 not found — check wiring"));
}
// Auto-start 45-minute class timer
countdownSecs = CLASS_DURATION_SECS;
timerLastTick = millis();
timerRunning = true;
Serial.println(F("=== Smart Classroom Ready ==="));
Serial.print(F("45-min timer started | Enrolled cards: "));
Serial.println(ENROLLED_COUNT);
Serial.println(F("Sensors: DHT11 | Water | Sound | MPU6050 | RFID"));
}
// ── Loop ──────────────────────────────────────────────────────
void loop() {
unsigned long now = millis();
// ── 1. DHT11 every 2 s ──────────────────────────────────────
if (now - lastSensor >= 2000) {
lastSensor = now;
float t = dht.readTemperature();
float h = dht.readHumidity();
if (!isnan(t)) lastT = t;
if (!isnan(h)) lastH = h;
}
// ── 2. Fan/heat LED ─────────────────────────────────────────
digitalWrite(FAN_LED_PIN, lastT >= TEMP_HOT_C ? HIGH : LOW);
// ── 3. Water level every 1 s ────────────────────────────────
if (now - lastWater >= 1000) {
lastWater = now;
waterLevel = readWater();
}
bool floodAlert = (lastH >= HUMID_FLOOD_PCT) && (waterLevel >= WATER_LEVEL_THRESH);
// ── 4. MPU6050 earthquake detection ─────────────────────────
int16_t ax, ay, az, gx, gy, gz;
mpu.getMotion6(&ax, &ay, &az, &gx, &gy, &gz);
// Subtract gravity (1g ≈ 16384 in ±2g range) from Z axis
int32_t mag = (int32_t)abs(ax) + abs(ay) + abs((int16_t)(az - 16384));
if (mag > QUAKE_THRESHOLD && !quakeAlert) {
quakeAlert = true;
quakeTime = now;
Serial.println(F("!! EARTHQUAKE ALERT — SEEK COVER !!"));
}
if (quakeAlert && (now - quakeTime >= 10000)) {
quakeAlert = false;
Serial.println(F("Quake alert cleared."));
}
// ── 5. Buzzer: quake > flood > dismissed ────────────────────
if (quakeAlert) {
// Rapid double-beep
uint8_t phase = (uint8_t)((now / 150) % 4);
digitalWrite(BUZZER_PIN, (phase == 0 || phase == 2) ? HIGH : LOW);
} else if (floodAlert) {
digitalWrite(BUZZER_PIN, (now / 500) % 2 == 0 ? HIGH : LOW);
} else if (!timerRunning && countdownSecs == 0) {
// Class dismissed — slow beep for 10 s then stop
unsigned long elapsed = now - timerLastTick;
if (elapsed < 10000) {
digitalWrite(BUZZER_PIN, (now / 600) % 2 == 0 ? HIGH : LOW);
} else {
digitalWrite(BUZZER_PIN, LOW);
}
} else {
digitalWrite(BUZZER_PIN, LOW);
}
// ── 6. Sound / noise detection ──────────────────────────────
bool soundHigh = (digitalRead(SOUND_DO_PIN) == HIGH);
if (soundHigh) {
if (noiseSince == 0) noiseSince = now;
noiseActive = (now - noiseSince >= NOISE_DURATION_MS);
} else {
noiseSince = 0;
noiseActive = false;
}
if (noiseActive) {
static bool noisePrinted = false;
if (!noisePrinted) {
Serial.println(F("NOISE ALERT — classroom too loud!"));
noisePrinted = true;
}
} else {
static bool noisePrinted = false;
noisePrinted = false;
}
// ── 7. RFID attendance ──────────────────────────────────────
if (rfid.PICC_IsNewCardPresent() && rfid.PICC_ReadCardSerial()) {
const char* name = lookupUid(rfid.uid);
if (name) {
attendanceCount++;
Serial.print(F("ATTENDANCE: "));
Serial.print(name);
Serial.print(F(" #"));
Serial.println(attendanceCount);
// Teacher card toggles DND
if (strcmp(name, "Teacher") == 0) {
dndActive = !dndActive;
Serial.print(F("DND: "));
Serial.println(dndActive ? F("ON") : F("OFF"));
}
} else {
Serial.print(F("UNKNOWN CARD UID: "));
for (byte i = 0; i < rfid.uid.size; i++) {
if (rfid.uid.uidByte[i] < 0x10) Serial.print('0');
Serial.print(rfid.uid.uidByte[i], HEX);
if (i < rfid.uid.size - 1) Serial.print(',');
}
Serial.println();
}
rfid.PICC_HaltA();
rfid.PCD_StopCrypto1();
}
// ── 8. DND LED ──────────────────────────────────────────────
digitalWrite(DND_LED_PIN, dndActive ? HIGH : LOW);
// ── 9. Countdown timer ──────────────────────────────────────
if (timerRunning) {
if (now - timerLastTick >= 1000) {
timerLastTick += 1000;
if (--countdownSecs <= 0) {
countdownSecs = 0;
timerRunning = false;
timerLastTick = now; // reuse as dismissed-at timestamp for buzzer
Serial.println(F("*** CLASS DISMISSED ***"));
}
}
showCountdown(countdownSecs);
} else if (countdownSecs == 0) {
// Flash 00:00
if ((now / 500) % 2 == 0) {
seg.showNumberDecEx(0, 0x40, true);
} else {
uint8_t blank[4] = {0, 0, 0, 0};
seg.setSegments(blank);
}
} else {
showSegDashes();
}
// ── 10. Serial heartbeat every 10 s ─────────────────────────
static unsigned long lastSerial = 0;
if (now - lastSerial >= 10000) {
lastSerial = now;
Serial.print(F("T:")); Serial.print(lastT, 1);
Serial.print(F("C H:")); Serial.print(lastH, 1);
Serial.print(F("% | Water:")); Serial.print(waterLevel);
Serial.print(F(" | Flood:")); Serial.print(floodAlert ? F("YES") : F("no"));
Serial.print(F(" | Quake:")); Serial.print(quakeAlert ? F("YES") : F("no"));
Serial.print(F(" | Noise:")); Serial.print(noiseActive ? F("YES") : F("no"));
Serial.print(F(" | DND:")); Serial.print(dndActive ? F("ON") : F("off"));
Serial.print(F(" | Timer:")); Serial.print(countdownSecs);
Serial.print(F("s | Attn:")); Serial.println(attendanceCount);
}
}“Deploy to device” opens this project in Schematik, where you can flash it to your board over USB.
Remix this project
Make it yours in one click
Open a full copy of this project in your own Schematik workspace — diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.