This ESP32 School Period Alarm System is a smart, customizable bell scheduler designed to automate school periods, recesses, and end-of-day notifications. With built-in real-time clock (RTC), web-based configuration, and OLED status display, it eliminates the need for manual bell ringing or mechanical timers. This project is ideal for schools, coaching centers, and even factories where regular timed alerts are essential, all built using affordable and readily available components.
โ Key Benefits
- โฑ Accurate Scheduling: Uses a DS3231 RTC to keep precise time.
- ๐ฑ Easy Configuration: Web interface allows dynamic editing of periods, recess, and off-time via a smartphone.
- ๐ Per-Day Customization: Different schedules for each weekday make it ideal for schools with varied timetables.
- ๐พ Non-Volatile Memory: Stores schedule in EEPROM โ settings remain saved after power loss.
- ๐ Automated Alerts: Buzzer rings automatically, reducing human error and increasing consistency.
- ๐ Clear Display: OLED screen shows real-time status and upcoming period alerts.
The ESP32 School Period Alarm System works as a smart scheduler and bell system for schools. Here’s a breakdown of how it functions:
๐ง Core Functionality
- ESP32 is the brain of the system, controlling timing, alarms, and the web interface.
- DS3231 RTC Module keeps accurate real-time clock data, even if the ESP32 resets.
- OLED 128×64 Display shows:
- Current time in 12-hour format
- Recess and off-time
- Upcoming periods (e.g.,
1 - 08:00 AM
,2 - 08:30 AM
, etc.)
- Buzzer rings:
- Short beeps indicating the period number (e.g., 3 beeps for 3rd period)
- Long continuous buzz for 30 seconds at recess and off time
๐ฒ Web Interface (via Smartphone)
- Accessed via ESP32โs Wi-Fi hotspot (
ESP32-Alarm
) - From a browser, you can:
- View and set recess time
- Set off-time
- Dynamically add/edit up to 12 periods per day
- Save settings to EEPROM
- JavaScript enables adding more period fields on the fly.
๐ Per-Day Scheduling
- Each day of the week has its own independent set of up to 12 periods
- Settings are remembered and retrieved daily based on RTCโs day-of-week
๐ Physical Controls
- Three buttons:
ADD
: Adds the current time as a new periodREMOVE
: Removes the last periodADJUST
: Cycles through periods to adjust time
- Optionally, rocker switch replaces a button for on/off or manual override
๐พ Persistent Memory
- All period times, recess, and off-time are stored in EEPROM
- Even after power loss, the system retains its schedule
๐ Daily Operation
- RTC gives current time.
- At scheduled times, the buzzer rings.
- Display updates with the next period info.
- User can update schedule anytime via the web interface.
Here’s a complete outline and implementation plan for your ESP32-based School Lecture Period Alarm System with:
โ Key Features:
- ESP32 microcontroller
- 128×64 I2C OLED display (SSD1306)
- Buzzer
- Push buttons to manually add/remove/adjust
- Web interface to configure periods remotely
- EEPROM to save and load period schedule
- RTC optional (ESP32 can use its own RTC + NTP sync if desired)
๐งฐ Hardware Components
Component | Notes |
---|---|
ESP32 Dev Board | Wi-Fi support |
OLED 128×64 I2C | SSD1306 |
Buzzer | Active buzzer to GPIO |
3 Push Buttons | For Add / Remove / Adjust |
Optional RTC | DS3231 (if no internet time) |
๐งฑ Wiring Summary
Component | ESP32 Pin |
---|---|
OLED SDA | GPIO 21 |
OLED SCL | GPIO 22 |
Buzzer | GPIO 27 |
Add Button | GPIO 32 |
Remove Button | GPIO 33 |
Adjust Button | GPIO 25 |
๐งพ Libraries Required
Install these in Arduino IDE:
Adafruit_SSD1306
Adafruit_GFX
WiFi
WebServer
EEPROM
(built-in)time.h
(for NTP time)
๐ง Program Structure
- Boot ESP32
- Load period schedule from EEPROM
- Sync time using NTP
- Start web server for configuration
- Continuously:
- Display time and next period on OLED
- Ring buzzer at period start
- Respond to button input
- Save changes to EEPROM
๐ป Web Interface
- ESP32 hosts a simple web form:
- Add/Remove periods
- Set start times (HH:MM)
- Save to EEPROM
This sketch includes:
- โ DS3231 RTC integration
- โ 128×64 OLED time + period display
- โ EEPROM saving/loading for 12 periods
- โ Buzzer beeping period number times
- โ Add/Remove/Adjust buttons
- โ 12-hour format
- โ Web interface to view/edit times
๐ก Tip:
Use an external pull-down resistor (~10kฮฉ) on each button if you get unreliable readings, or ensure clean debouncing if needed.
โ Arduino School Periods System (complete sketch)
// ESP32 School Period Alarm System with Recess, Off-Time & Day Scheduling
// Features: DS3231 RTC, 128x64 OLED, EEPROM, Web Interface, Buzzer, Per-Day Scheduling
#include <Wire.h>
#include <RTClib.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <WiFi.h>
#include <WebServer.h>
#include <EEPROM.h>
// ==== CONFIGURATION ====
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define I2C_SDA 21
#define I2C_SCL 22
#define BUZZER_PIN 27
#define BTN_ADD 32
#define BTN_REM 33
#define BTN_ADJ 25
#define MAX_PERIODS 12
#define DAYS 7
#define EEPROM_SIZE (MAX_PERIODS * 2 * DAYS + 4) // 4 extra bytes for recess and off times
const char* ssid = "ESP32-Alarm";
const char* password = "12345678";
RTC_DS3231 rtc;
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
WebServer server(80);
uint8_t periods[DAYS][MAX_PERIODS][2];
int periodCounts[DAYS];
bool alarmTriggered[DAYS][MAX_PERIODS];
bool recessTriggered = false;
bool offTriggered = false;
uint8_t recessTime[2];
uint8_t offTime[2];
unsigned long lastDebounce = 0;
int selectedPeriod = 0;
unsigned long lastPageChange = 0;
int currentPage = 0;
void loadPeriodsFromEEPROM();
void savePeriodsToEEPROM();
void ringBuzzer(int times);
void ringBuzzerLong();
void handleWebRoot();
void handleWebSubmit();
void updateDisplay();
void checkPeriodTrigger(DateTime now);
void handleButtons(DateTime now);
void sortPeriods();
void setup() {
Serial.begin(115200);
Wire.begin(I2C_SDA, I2C_SCL);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(BTN_ADD, INPUT_PULLUP);
pinMode(BTN_REM, INPUT_PULLUP);
pinMode(BTN_ADJ, INPUT_PULLUP);
EEPROM.begin(EEPROM_SIZE);
if (!rtc.begin()) while (1);
if (rtc.lostPower()) rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) while (1);
loadPeriodsFromEEPROM();
for (int d = 0; d < DAYS; d++) sortPeriods(d);
for (int d = 0; d < DAYS; d++) for (int i = 0; i < MAX_PERIODS; i++) alarmTriggered[d][i] = false;
WiFi.softAP(ssid, password);
server.on("/", handleWebRoot);
server.on("/submit", handleWebSubmit);
server.begin();
}
void loop() {
DateTime now = rtc.now();
updateDisplay();
checkPeriodTrigger(now);
handleButtons(now);
server.handleClient();
delay(200);
}
void updateDisplay() {
DateTime now = rtc.now();
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(WHITE);
char buf[20];
int hr = now.hour();
bool pm = false;
if (hr >= 12) pm = true;
if (hr == 0) hr = 12;
else if (hr > 12) hr -= 12;
sprintf(buf, "Time: %02d:%02d %s", hr, now.minute(), pm ? "PM" : "AM");
display.setCursor(0, 0);
display.print(buf);
display.setCursor(0, 10);
sprintf(buf, "Recess: %02d:%02d", recessTime[0], recessTime[1]);
display.print(buf);
display.setCursor(0, 20);
sprintf(buf, "Off: %02d:%02d", offTime[0], offTime[1]);
display.print(buf);
// Update page every 5 seconds
if (millis() - lastPageChange > 5000) {
currentPage = (currentPage + 1) % ((periodCount + 5) / 6); // 6 per page
lastPageChange = millis();
}
// Show up to 6 periods per page
int startIdx = currentPage * 6;
int endIdx = min(startIdx + 6, periodCount);
for (int i = startIdx, y = 30; i < endIdx; i++, y += 6) {
sprintf(buf, "P%d - %02d:%02d", i + 1, periods[i][0], periods[i][1]);
display.setCursor(0, y);
display.print(buf);
}
display.display();
}
void checkPeriodTrigger(DateTime now) {
int d = now.dayOfTheWeek();
for (int i = 0; i < periodCounts[d]; i++) {
if (now.hour() == periods[d][i][0] && now.minute() == periods[d][i][1] && now.second() == 0) {
if (!alarmTriggered[d][i]) {
ringBuzzer(i + 1);
alarmTriggered[d][i] = true;
}
} else if (now.minute() != periods[d][i][1]) {
alarmTriggered[d][i] = false;
}
}
if (now.hour() == recessTime[0] && now.minute() == recessTime[1] && now.second() == 0 && !recessTriggered) {
ringBuzzerLong(); recessTriggered = true;
} else if (now.minute() != recessTime[1]) recessTriggered = false;
if (now.hour() == offTime[0] && now.minute() == offTime[1] && now.second() == 0 && !offTriggered) {
ringBuzzerLong(); offTriggered = true;
} else if (now.minute() != offTime[1]) offTriggered = false;
}
void ringBuzzer(int times) {
for (int i = 0; i < times; i++) {
digitalWrite(BUZZER_PIN, HIGH); delay(200);
digitalWrite(BUZZER_PIN, LOW); delay(200);
}
}
void ringBuzzerLong() {
for (int i = 0; i < 30; i++) {
digitalWrite(BUZZER_PIN, HIGH); delay(500);
digitalWrite(BUZZER_PIN, LOW); delay(500);
}
}
void loadPeriodsFromEEPROM() {
int addr = 0;
for (int d = 0; d < DAYS; d++) {
periodCounts[d] = 0;
for (int i = 0; i < MAX_PERIODS; i++) {
uint8_t h = EEPROM.read(addr++);
uint8_t m = EEPROM.read(addr++);
if (h < 24 && m < 60) {
periods[d][periodCounts[d]][0] = h;
periods[d][periodCounts[d]][1] = m;
periodCounts[d]++;
}
}
}
recessTime[0] = EEPROM.read(addr++);
recessTime[1] = EEPROM.read(addr++);
offTime[0] = EEPROM.read(addr++);
offTime[1] = EEPROM.read(addr++);
}
void savePeriodsToEEPROM() {
int addr = 0;
for (int d = 0; d < DAYS; d++) {
for (int i = 0; i < MAX_PERIODS; i++) {
if (i < periodCounts[d]) {
EEPROM.write(addr++, periods[d][i][0]);
EEPROM.write(addr++, periods[d][i][1]);
} else {
EEPROM.write(addr++, 0xFF);
EEPROM.write(addr++, 0xFF);
}
}
}
EEPROM.write(addr++, recessTime[0]);
EEPROM.write(addr++, recessTime[1]);
EEPROM.write(addr++, offTime[0]);
EEPROM.write(addr++, offTime[1]);
EEPROM.commit();
for (int d = 0; d < DAYS; d++) sortPeriods(d);
}
void sortPeriods(int d) {
for (int i = 0; i < periodCounts[d] - 1; i++) {
for (int j = 0; j < periodCounts[d] - i - 1; j++) {
if (periods[d][j][0] > periods[d][j + 1][0] ||
(periods[d][j][0] == periods[d][j + 1][0] && periods[d][j][1] > periods[d][j + 1][1])) {
uint8_t tmpH = periods[d][j][0];
uint8_t tmpM = periods[d][j][1];
periods[d][j][0] = periods[d][j + 1][0];
periods[d][j][1] = periods[d][j + 1][1];
periods[d][j + 1][0] = tmpH;
periods[d][j + 1][1] = tmpM;
}
}
}
}
void handleButtons(DateTime now) {
int d = now.dayOfTheWeek();
if (millis() - lastDebounce < 300) return;
if (digitalRead(BTN_ADD) == LOW && periodCounts[d] < MAX_PERIODS) {
periods[d][periodCounts[d]][0] = now.hour();
periods[d][periodCounts[d]][1] = (now.minute() + 1) % 60;
periodCounts[d]++;
savePeriodsToEEPROM();
lastDebounce = millis();
}
if (digitalRead(BTN_REM) == LOW && periodCounts[d] > 0) {
periodCounts[d]--;
savePeriodsToEEPROM();
lastDebounce = millis();
}
if (digitalRead(BTN_ADJ) == LOW && periodCounts[d] > 0) {
selectedPeriod = (selectedPeriod + 1) % periodCounts[d];
periods[d][selectedPeriod][0] = now.hour();
periods[d][selectedPeriod][1] = now.minute();
savePeriodsToEEPROM();
lastDebounce = millis();
}
}
void handleWebRoot() {
String html = "<html><body><h2>Set Period Times</h2><form action='/submit' method='post'>";
int d = rtc.now().dayOfTheWeek();
for (int i = 0; i < periodCounts[d]; i++) {
html += "Period " + String(i + 1) + ": <input name='p" + String(i) + "' value='" +
String(periods[d][i][0]) + ":" + String(periods[d][i][1]) + "'><br>";
}
html += "<br>Recess Time: <input name='recess' value='" + String(recessTime[0]) + ":" + String(recessTime[1]) + "'><br>";
html += "Off Time: <input name='off' value='" + String(offTime[0]) + ":" + String(offTime[1]) + "'><br><br>";
html += "<input type='submit' value='Save'></form></body></html>";
server.send(200, "text/html", html);
}
void handleWebSubmit() {
int d = rtc.now().dayOfTheWeek();
for (int i = 0; i < MAX_PERIODS; i++) {
if (server.hasArg("p" + String(i))) {
String val = server.arg("p" + String(i));
int colon = val.indexOf(":");
if (colon > 0) {
int h = val.substring(0, colon).toInt();
int m = val.substring(colon + 1).toInt();
if (h < 24 && m < 60) {
periods[d][i][0] = h;
periods[d][i][1] = m;
}
}
} else {
if (i < periodCounts[d]) {
periods[d][i][0] = 0xFF;
periods[d][i][1] = 0xFF;
}
}
}
periodCounts[d] = 0;
for (int i = 0; i < MAX_PERIODS; i++) {
if (periods[d][i][0] < 24 && periods[d][i][1] < 60) periodCounts[d]++;
}
if (server.hasArg("recess")) {
String val = server.arg("recess");
int colon = val.indexOf(":");
if (colon > 0) {
recessTime[0] = val.substring(0, colon).toInt();
recessTime[1] = val.substring(colon + 1).toInt();
}
}
if (server.hasArg("off")) {
String val = server.arg("off");
int colon = val.indexOf(":");
if (colon > 0) {
offTime[0] = val.substring(0, colon).toInt();
offTime[1] = val.substring(colon + 1).toInt();
}
}
savePeriodsToEEPROM();
server.sendHeader("Location", "/");
server.send(303);
}
โ Conclusion
The ESP32 School Period Alarm System offers an efficient, modern solution for automating bell schedules in educational institutions. With real-time accuracy, day-wise flexibility, smartphone-based control, and reliable EEPROM storage, it simplifies school management and ensures punctuality.
Its intuitive interface and visual OLED feedback make it user-friendly for staff, while its customizable nature ensures it fits any school’s unique timetable. By integrating smart electronics into a practical application, this project showcases how technology can streamline daily operations and enhance discipline in academic environments.
๐ Help Us Spread the Word!
If you found this ESP32 School Period Alarm System useful or inspiring, please consider sharing it with your friends, colleagues, or fellow makers on social media. Your support helps promote practical tech solutions and encourages more open-source innovation in education!
๐ฑ๐ก Share on:
- ๐ Facebook
- ๐ฆ Twitter
- ๐ธ Instagram
- ๐ Pinterest
- ๐ฌ WhatsApp
Together, letโs make schools smarter and more efficient! ๐๐๐
Feel free to include a picture of your own build and tag your post with #ESP32SchoolAlarm or #SmartSchoolBell!
๐ฏ Search-Intent Specific Keywords
- ESP32 Smart School Bell System with OLED, Web UI & RTC
- DIY School Period Alarm Using ESP32 and DS3231 RTC
- Automatic School Bell System with ESP32 and OLED Display
- Build a Wi-Fi School Bell Timer with ESP32 + Web Control
- ESP32-Based Timetable Alarm with Day Scheduling & Recess Alerts
- ESP32 Period Scheduler with Smartphone Web Interface
- Smart School Alarm with Recess & Off-Time Alerts
- Customizable School Bell System with EEPROM & RTC
- OLED Display Bell Timer with ESP32 and Web Management
- Create a Daily School Bell System with ESP32 (12 Periods Max)
- How to Make an Automatic School Period Alarm with ESP32
- ESP32 School Bell Project for Class Period Scheduling
- Web-Controlled School Alarm Using ESP32 and OLED
- ESP32 Wi-Fi Bell Timer with EEPROM Settings and RTC
- ESP32 Class Bell Scheduler with Day-of-Week Configuration