Top 5 ESP32 Projects for School Students (With Code & Circuit)

Top 5 ESP32 Projects for School Students (With Code & Circuit)

The ESP32 is the perfect microcontroller for school science projects — it has built-in WiFi, Bluetooth, dual-core processing, and costs under $5. These 5 projects are impressive enough to stand out at any science fair, simple enough to build in a weekend, and teach real-world electronics and programming skills.

Project 1 — Smart Attendance System with RFID

Project Description

This project uses an RFID reader (RC522) connected to an ESP32 to scan student ID cards and log attendance automatically to a Google Sheet via WiFi. When a student taps their card, their name and timestamp are recorded instantly — no paper registers needed. It is a great demonstration of IoT, cloud integration, and practical automation.

Components needed: ESP32, RC522 RFID module, RFID cards/tags, buzzer, green LED, red LED, 220Ω resistors, breadboard, jumper wires.

Circuit Description

RC522 PinESP32 Pin
SDA (SS)GPIO 5
SCKGPIO 18
MOSIGPIO 23
MISOGPIO 19
RSTGPIO 27
3.3V3.3V
GNDGND

Connect the green LED (with 220Ω resistor) to GPIO 26 and the red LED to GPIO 25. The buzzer connects to GPIO 33.

Code

#include <SPI.h>
#include <MFRC522.h>
#include <WiFi.h>
#include <HTTPClient.h>

#define SS_PIN   5
#define RST_PIN  27
#define GREEN_LED 26
#define RED_LED   25
#define BUZZER    33

MFRC522 rfid(SS_PIN, RST_PIN);

const char* ssid     = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
// Google Apps Script Web App URL
const char* scriptURL = "https://script.google.com/macros/s/YOUR_SCRIPT_ID/exec";

// Registered card UIDs and names
String knownUIDs[]  = {"A1B2C3D4", "E5F60718"};
String knownNames[] = {"Alice",    "Bob"};
int totalStudents   = 2;

void setup() {
  Serial.begin(115200);
  SPI.begin();
  rfid.PCD_Init();
  pinMode(GREEN_LED, OUTPUT);
  pinMode(RED_LED,   OUTPUT);
  pinMode(BUZZER,    OUTPUT);

  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500); Serial.print(".");
  }
  Serial.println("\nConnected!");
}

void beep(int times) {
  for (int i = 0; i < times; i++) {
    digitalWrite(BUZZER, HIGH); delay(100);
    digitalWrite(BUZZER, LOW);  delay(100);
  }
}

void logAttendance(String name, String uid) {
  if (WiFi.status() == WL_CONNECTED) {
    HTTPClient http;
    String url = String(scriptURL) + "?name=" + name + "&uid=" + uid;
    http.begin(url);
    http.GET();
    http.end();
  }
}

void loop() {
  if (!rfid.PICC_IsNewCardPresent() || !rfid.PICC_ReadCardSerial()) return;

  String uid = "";
  for (byte i = 0; i < rfid.uid.size; i++) {
    uid += String(rfid.uid.uidByte[i], HEX);
  }
  uid.toUpperCase();
  Serial.println("Card UID: " + uid);

  bool found = false;
  for (int i = 0; i < totalStudents; i++) {
    if (uid == knownUIDs[i]) {
      Serial.println("Welcome, " + knownNames[i]);
      digitalWrite(GREEN_LED, HIGH); beep(1);
      logAttendance(knownNames[i], uid);
      delay(1000);
      digitalWrite(GREEN_LED, LOW);
      found = true;
      break;
    }
  }
  if (!found) {
    Serial.println("Unknown card!");
    digitalWrite(RED_LED, HIGH); beep(3);
    delay(1000);
    digitalWrite(RED_LED, LOW);
  }
  rfid.PICC_HaltA();
}

Conclusion

This project teaches RFID communication over SPI, WiFi HTTP requests, and cloud data logging — all core IoT skills. Next step: add an OLED display to show the student's name on tap, or build a web dashboard to view attendance records live.

Project 2 — Wireless Weather Station

Project Description

Build a weather station that reads temperature, humidity, and atmospheric pressure, then displays the data on a local web page served from the ESP32 itself. Any phone or laptop on the same WiFi network can open the page and see live readings — no internet or cloud account required. Great for understanding sensors, web servers, and data presentation.

Components needed: ESP32, DHT22 sensor, BMP280 pressure sensor, 10kΩ resistor, breadboard, jumper wires.

Circuit Description

ComponentPinESP32 Pin
DHT22DATAGPIO 4
DHT22VCC3.3V
DHT22GNDGND
BMP280SDAGPIO 21
BMP280SCLGPIO 22
BMP280VCC3.3V
BMP280GNDGND

Place a 10kΩ pull-up resistor between the DHT22 DATA pin and 3.3V.

Code

#include <WiFi.h>
#include <WebServer.h>
#include <DHT.h>
#include <Adafruit_BMP280.h>

const char* ssid     = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";

#define DHTPIN  4
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
Adafruit_BMP280 bmp;
WebServer server(80);

void handleRoot() {
  float temp     = dht.readTemperature();
  float humidity = dht.readHumidity();
  float pressure = bmp.readPressure() / 100.0F; // hPa

  String html = "<!DOCTYPE html><html><head>";
  html += "<meta charset='UTF-8'>";
  html += "<meta http-equiv='refresh' content='10'>"; // auto-refresh every 10s
  html += "<title>Weather Station</title>";
  html += "<style>body{font-family:sans-serif;text-align:center;background:#f0f4f8;}";
  html += ".card{display:inline-block;margin:20px;padding:30px;background:#fff;border-radius:12px;box-shadow:0 2px 8px rgba(0,0,0,0.1);}";
  html += "h1{color:#333;} .value{font-size:2.5em;color:#2196F3;font-weight:bold;}";
  html += ".unit{font-size:1em;color:#666;}</style></head><body>";
  html += "<h1>🌤 Weather Station</h1>";
  html += "<div class='card'><p>Temperature</p><div class='value'>" + String(temp,1) + "</div><div class='unit'>°C</div></div>";
  html += "<div class='card'><p>Humidity</p><div class='value'>" + String(humidity,1) + "</div><div class='unit'>%</div></div>";
  html += "<div class='card'><p>Pressure</p><div class='value'>" + String(pressure,1) + "</div><div class='unit'>hPa</div></div>";
  html += "</body></html>";
  server.send(200, "text/html", html);
}

void setup() {
  Serial.begin(115200);
  dht.begin();
  bmp.begin(0x76);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
  Serial.println("\nIP: " + WiFi.localIP().toString());
  server.on("/", handleRoot);
  server.begin();
}

void loop() {
  server.handleClient();
}

Conclusion

You've built a fully self-hosted weather station with a live web interface — no cloud required. Next steps: add a small OLED screen for standalone display, log readings to an SD card every hour, or push data to ThingSpeak for long-term graphing.

Project 3 — Automatic Plant Watering System

Project Description

This system monitors soil moisture with a capacitive sensor and automatically activates a mini water pump when the soil gets too dry. It also sends a Telegram message to your phone whenever it waters the plant. Perfect for demonstrating analog sensors, relay control, and IoT notifications in a school project.

Components needed: ESP32, capacitive soil moisture sensor, 5V mini submersible water pump, 5V relay module, small water tube, breadboard, jumper wires.

Circuit Description

ComponentPinESP32 Pin
Soil SensorAOUTGPIO 34 (ADC)
Soil SensorVCC3.3V
Soil SensorGNDGND
Relay ModuleINGPIO 26
Relay ModuleVCC5V (Vin)
Relay ModuleGNDGND

Connect the water pump to the relay's NO (Normally Open) and COM terminals. Power the pump from an external 5V source — do not power it from the ESP32's 3.3V pin.

Code

#include <WiFi.h>
#include <HTTPClient.h>

const char* ssid      = "YOUR_WIFI_SSID";
const char* password  = "YOUR_WIFI_PASSWORD";
const char* botToken  = "YOUR_TELEGRAM_BOT_TOKEN";
const char* chatID    = "YOUR_TELEGRAM_CHAT_ID";

#define SOIL_PIN   34
#define RELAY_PIN  26

// Calibrate these values for your sensor
// 4095 = bone dry, ~1500 = soaking wet (capacitive sensors vary)
#define DRY_THRESHOLD  2800

void sendTelegram(String message) {
  if (WiFi.status() != WL_CONNECTED) return;
  HTTPClient http;
  String url = "https://api.telegram.org/bot" + String(botToken) +
               "/sendMessage?chat_id=" + String(chatID) +
               "&text=" + message;
  http.begin(url);
  http.GET();
  http.end();
}

void setup() {
  Serial.begin(115200);
  pinMode(RELAY_PIN, OUTPUT);
  digitalWrite(RELAY_PIN, LOW); // pump off initially

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
  Serial.println("\nConnected. IP: " + WiFi.localIP().toString());
}

void loop() {
  int moisture = analogRead(SOIL_PIN);
  Serial.println("Soil moisture: " + String(moisture));

  if (moisture > DRY_THRESHOLD) {
    Serial.println("Soil dry! Watering...");
    digitalWrite(RELAY_PIN, HIGH); // turn pump ON
    delay(3000);                   // water for 3 seconds
    digitalWrite(RELAY_PIN, LOW);  // turn pump OFF
    sendTelegram("🌱 Plant watered! Soil moisture was " + String(moisture));
    delay(60000); // wait 1 minute before checking again
  }
  delay(5000); // check every 5 seconds
}

Conclusion

This project combines analog sensing, relay-controlled actuators, and IoT notifications — three fundamental building blocks of home automation. Next step: add a DHT22 to also monitor ambient temperature, or build a dashboard on ThingSpeak to graph soil moisture over time.

Project 4 — Quiz Buzzer System

Project Description

Build a classroom quiz buzzer system for up to 4 teams. When a team presses their button, their buzzer sounds, their LED lights up, and a 7-segment display (or serial output) shows which team buzzed first. All other buttons are locked out until the host resets the round. A fun, practical project for electronics and programming classes.

Components needed: ESP32, 4 push buttons, 4 LEDs (different colors), 4 × 220Ω resistors, 4 × 10kΩ resistors, buzzer, breadboard, jumper wires.

Circuit Description

ComponentESP32 Pin
Team 1 ButtonGPIO 13 (INPUT_PULLUP)
Team 2 ButtonGPIO 12 (INPUT_PULLUP)
Team 3 ButtonGPIO 14 (INPUT_PULLUP)
Team 4 ButtonGPIO 27 (INPUT_PULLUP)
Reset ButtonGPIO 26 (INPUT_PULLUP)
Team 1 LEDGPIO 32
Team 2 LEDGPIO 33
Team 3 LEDGPIO 25
Team 4 LEDGPIO 4
BuzzerGPIO 2

Use INPUT_PULLUP for all buttons — wire one side to the GPIO pin and the other to GND. Each LED needs a 220Ω resistor in series to GND.

Code

const int buttonPins[] = {13, 12, 14, 27};
const int ledPins[]    = {32, 33, 25, 4};
const int resetPin     = 26;
const int buzzerPin    = 2;

int winner   = -1;
bool locked  = false;

void buzz(int times, int duration = 150) {
  for (int i = 0; i < times; i++) {
    digitalWrite(buzzerPin, HIGH); delay(duration);
    digitalWrite(buzzerPin, LOW);  delay(100);
  }
}

void setup() {
  Serial.begin(115200);
  for (int i = 0; i < 4; i++) {
    pinMode(buttonPins[i], INPUT_PULLUP);
    pinMode(ledPins[i],    OUTPUT);
  }
  pinMode(resetPin,  INPUT_PULLUP);
  pinMode(buzzerPin, OUTPUT);
  Serial.println("Quiz Buzzer Ready! Press a button...");
}

void loop() {
  // Check reset button
  if (digitalRead(resetPin) == LOW) {
    locked = false;
    winner = -1;
    for (int i = 0; i < 4; i++) digitalWrite(ledPins[i], LOW);
    Serial.println("--- RESET: Ready for next question ---");
    delay(500);
  }

  if (!locked) {
    for (int i = 0; i < 4; i++) {
      if (digitalRead(buttonPins[i]) == LOW) {
        winner = i + 1;
        locked = true;
        digitalWrite(ledPins[i], HIGH);
        buzz(2);
        Serial.println(">>> TEAM " + String(winner) + " BUZZED FIRST!");
        delay(300); // debounce
        break;
      }
    }
  }
}

Conclusion

This project teaches digital input handling, debouncing, and state locking logic. Next step: add a 7-segment display to show the team number visually, or connect it to WiFi to display the winner on a browser scoreboard.

Project 5 — Air Quality Monitor

Project Description

This air quality monitor uses an MQ-135 gas sensor to detect harmful gases (CO2, ammonia, benzene, smoke) and a DHT22 for temperature and humidity. Readings are shown on a 0.96" OLED display and an alarm triggers when air quality drops below a safe threshold. An excellent project for environmental science topics.

Components needed: ESP32, MQ-135 gas sensor, DHT22, 0.96" I2C OLED display (SSD1306), buzzer, red LED, 220Ω resistor, breadboard, jumper wires.

Circuit Description

ComponentPinESP32 Pin
MQ-135AOUTGPIO 34
MQ-135VCC5V (Vin)
MQ-135GNDGND
DHT22DATAGPIO 4
OLEDSDAGPIO 21
OLEDSCLGPIO 22
Buzzer+GPIO 26
Red LED+GPIO 27

Code

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <DHT.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET   -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

#define DHTPIN    4
#define DHTTYPE   DHT22
DHT dht(DHTPIN, DHTTYPE);

#define MQ135_PIN  34
#define BUZZER_PIN 26
#define LED_PIN    27

// Above this raw ADC value = poor air quality (calibrate for your sensor)
#define AIR_THRESHOLD 2000

void setup() {
  Serial.begin(115200);
  dht.begin();
  pinMode(BUZZER_PIN, OUTPUT);
  pinMode(LED_PIN,    OUTPUT);

  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(1);
  display.setCursor(10, 25);
  display.println("Air Quality Monitor");
  display.display();
  delay(2000);
}

void loop() {
  int   airValue = analogRead(MQ135_PIN);
  float temp     = dht.readTemperature();
  float hum      = dht.readHumidity();
  String status  = (airValue > AIR_THRESHOLD) ? "POOR" : "GOOD";

  Serial.printf("Air: %d | Temp: %.1f C | Hum: %.1f%% | Status: %s\n",
                airValue, temp, hum, status.c_str());

  display.clearDisplay();
  display.setTextSize(1);
  display.setCursor(0, 0);  display.println("-- Air Quality --");
  display.setCursor(0, 16); display.printf("Air Value: %d", airValue);
  display.setCursor(0, 28); display.printf("Temp:  %.1f C", temp);
  display.setCursor(0, 40); display.printf("Hum:   %.1f %%", hum);
  display.setTextSize(2);
  display.setCursor(20, 50);
  display.println(status == "POOR" ? "!! POOR !!" : "  GOOD  ");
  display.display();

  if (airValue > AIR_THRESHOLD) {
    digitalWrite(LED_PIN, HIGH);
    digitalWrite(BUZZER_PIN, HIGH); delay(200);
    digitalWrite(BUZZER_PIN, LOW);
  } else {
    digitalWrite(LED_PIN, LOW);
  }
  delay(2000);
}

Conclusion

You've built a real environmental monitoring tool — the kind of sensor array used in industrial safety systems. Next steps: push readings to a ThingSpeak dashboard over WiFi, or add a second MQ sensor (MQ-7 for carbon monoxide) for a more complete air quality profile.


More ESP32 & Arduino Project Guides

Post a Comment

0 Comments