ESP32 Smart Doorbell with Notifications with step by step instructions

ESP32 Smart Doorbell with Notifications | MakeMindz Tutorial
🔔

ESP32 IoT Projects · Intermediate

ESP32 Smart Doorbell with Notifications

Build an intelligent doorbell with motion detection, camera simulation, OLED display, and a live web dashboard — all running on your ESP32.

⚡ ESP32 + WiFi 🎯 Intermediate Level 🕒 ~45 min build 🔧 Wokwi Ready

This project turns an ESP32 into a fully-featured smart doorbell. When someone presses the button or triggers the PIR motion sensor, the system plays a chime, activates the camera indicator, logs the event, updates the OLED display, and sends a simulated notification — all while serving a live web dashboard over WiFi.

Perfect for learning how to combine multiple peripherals with WiFi connectivity on the ESP32, while building something genuinely useful for home security.

ESP32 WiFi PIR Sensor OLED SSD1306 WebServer Buzzer Push Button Light Sensor Wokwi
🔔
Push Button Doorbell Debounced button triggers chime melody & notification
👁️
PIR Motion Detection Auto-activates camera and sends alert on motion
📷
Camera Simulation Camera LED triggers and logs simulated captures
🖥️
OLED Dashboard Live visitor count, door status & night mode display
🌐
Web Dashboard Remote monitoring with lock/unlock controls via browser
📱
Instant Notifications Simulated Email, SMS & push alerts on every event
🌙
Night Mode Light sensor auto-activates LED when dark
🔒
Remote Door Lock Lock or unlock door relay from the web interface
Component Quantity Purpose
ESP32 DevKit V11Main microcontroller with WiFi
Push Button (Blue)1Doorbell trigger
PIR Motion Sensor1Visitor detection
SSD1306 OLED (128×64)1Status display (I2C 0x3C)
Passive Buzzer1Doorbell chime melody
LEDs (Red, Green, Blue)3RGB status indicator
LED Yellow1Camera active indicator
LED White1Door lock status
220Ω Resistors5Current limiting for LEDs
Potentiometer1Light sensor simulation
💡
In a real build, replace the potentiometer with an LDR (light-dependent resistor) + 10kΩ resistor divider, and add a relay module for actual door lock control.
Component ESP32 Pin Wire Color
Doorbell ButtonD14 (INPUT_PULLUP)🔵 Blue
PIR Sensor OUTD13🟠 Orange
PIR VCC / GND3V3 / GND🔴🖤
Red LED (via 220Ω)D26🔴 Red
Green LED (via 220Ω)D27🟢 Green
Blue LED (via 220Ω)D12🔵 Blue
Camera LED (via 220Ω)D33🟡 Yellow
Door Lock LED (via 220Ω)D32⚪ White
BuzzerD25🟣 Purple
OLED SDAD21🔵 Blue
OLED SCLD22🟡 Yellow
OLED VCC / GND3V3 / GND🔴🖤
Light Sensor SIGD34 (ADC)🟢 Green

Copy this into your diagram.json file in Wokwi to instantly load the full circuit.

diagram.json
{
  "version": 1,
  "author": "ESP32 Smart Doorbell",
  "editor": "wokwi",
  "parts": [
    { "type": "wokwi-esp32-devkit-v1", "id": "esp", "top": 0, "left": 0, "attrs": {} },
    { "type": "wokwi-pushbutton", "id": "btn1", "top": -124.8, "left": 249.6,
      "attrs": { "color": "blue", "label": "DOORBELL" } },
    { "type": "wokwi-pir-motion-sensor", "id": "pir1", "top": -105.49, "left": 345.31, "attrs": {} },
    { "type": "wokwi-led", "id": "red_led", "top": -124.8, "left": 480,
      "attrs": { "color": "red", "lightColor": "red", "label": "R" } },
    { "type": "wokwi-resistor", "id": "r1", "top": -67.2, "left": 480, "rotate": 90,
      "attrs": { "value": "220" } },
    { "type": "wokwi-led", "id": "green_led", "top": -124.8, "left": 556.8,
      "attrs": { "color": "green", "lightColor": "green", "label": "G" } },
    { "type": "wokwi-resistor", "id": "r2", "top": -67.2, "left": 556.8, "rotate": 90,
      "attrs": { "value": "220" } },
    { "type": "wokwi-led", "id": "blue_led", "top": -124.8, "left": 633.6,
      "attrs": { "color": "blue", "lightColor": "blue", "label": "B" } },
    { "type": "wokwi-resistor", "id": "r3", "top": -67.2, "left": 633.6, "rotate": 90,
      "attrs": { "value": "220" } },
    { "type": "wokwi-led", "id": "camera_led", "top": 38.4, "left": 480,
      "attrs": { "color": "yellow", "lightColor": "yellow", "label": "Camera" } },
    { "type": "wokwi-resistor", "id": "r4", "top": 96, "left": 480, "rotate": 90,
      "attrs": { "value": "220" } },
    { "type": "wokwi-led", "id": "lock_led", "top": 38.4, "left": 556.8,
      "attrs": { "color": "white", "lightColor": "white", "label": "Door Lock" } },
    { "type": "wokwi-resistor", "id": "r5", "top": 96, "left": 556.8, "rotate": 90,
      "attrs": { "value": "220" } },
    { "type": "wokwi-buzzer", "id": "bz1", "top": 172.8, "left": 518.4,
      "attrs": { "volume": "0.5" } },
    { "type": "wokwi-ssd1306", "id": "oled1", "top": 192, "left": 249.6,
      "attrs": { "i2cAddress": "0x3C" } },
    { "type": "wokwi-potentiometer", "id": "light1", "top": 86.4, "left": 326.4, "rotate": 180,
      "attrs": { "label": "Light Sensor" } }
  ],
  "connections": [
    ["esp:TX0", "$serialMonitor:RX", "", []],
    ["esp:RX0", "$serialMonitor:TX", "", []],
    ["btn1:1.l", "esp:D14", "blue", ["v0"]],
    ["btn1:2.r", "esp:GND.1", "black", ["v0"]],
    ["pir1:VCC", "esp:3V3", "red", ["v0"]],
    ["pir1:GND", "esp:GND.1", "black", ["v0"]],
    ["pir1:OUT", "esp:D13", "orange", ["v0"]],
    ["red_led:A", "esp:D26", "red", ["v0"]],
    ["red_led:C", "r1:1", "red", ["v0"]],
    ["r1:2", "esp:GND.1", "black", ["v0"]],
    ["green_led:A", "esp:D27", "green", ["v0"]],
    ["green_led:C", "r2:1", "green", ["v0"]],
    ["r2:2", "esp:GND.1", "black", ["v0"]],
    ["blue_led:A", "esp:D12", "blue", ["v0"]],
    ["blue_led:C", "r3:1", "blue", ["v0"]],
    ["r3:2", "esp:GND.1", "black", ["v0"]],
    ["camera_led:A", "esp:D33", "yellow", ["v0"]],
    ["camera_led:C", "r4:1", "yellow", ["v0"]],
    ["r4:2", "esp:GND.2", "black", ["v0"]],
    ["lock_led:A", "esp:D32", "white", ["v0"]],
    ["lock_led:C", "r5:1", "white", ["v0"]],
    ["r5:2", "esp:GND.2", "black", ["v0"]],
    ["bz1:1", "esp:D25", "purple", ["v0"]],
    ["bz1:2", "esp:GND.2", "black", ["v0"]],
    ["oled1:VCC", "esp:3V3", "red", ["h0"]],
    ["oled1:GND", "esp:GND.2", "black", ["h0"]],
    ["oled1:SDA", "esp:D21", "blue", ["h0"]],
    ["oled1:SCL", "esp:D22", "yellow", ["h0"]],
    ["light1:GND", "esp:GND.2", "black", ["v0"]],
    ["light1:VCC", "esp:3V3", "red", ["v0"]],
    ["light1:SIG", "esp:D34", "green", ["v0"]]
  ],
  "dependencies": {}
}
📦
Required Libraries: Install via Arduino Library Manager — Adafruit GFX Library and Adafruit SSD1306. WiFi and WebServer are built into the ESP32 Arduino core.
sketch.ino
/*
 * ESP32 Smart Doorbell with Notifications
 * MakeMindz.com — IoT Project Series
 * Features: Doorbell button, PIR motion, OLED, RGB LED,
 *           Buzzer chime, Web dashboard, Visitor log
 */

#include <WiFi.h>
#include <WebServer.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// ── WiFi Credentials ──────────────────────────
const char* ssid     = "Wokwi-GUEST";
const char* password = "";

// ── Web Server ─────────────────────────────────
WebServer server(80);

// ── Pin Definitions ────────────────────────────
#define DOORBELL_BUTTON_PIN  14   // Doorbell push button
#define PIR_SENSOR_PIN       13   // PIR motion sensor
#define BUZZER_PIN           25   // Doorbell chime
#define RED_LED_PIN          26   // RGB Red
#define GREEN_LED_PIN        27   // RGB Green
#define BLUE_LED_PIN         12   // RGB Blue
#define CAMERA_TRIGGER_PIN   33   // Camera simulation LED
#define RELAY_DOOR_PIN       32   // Door lock relay
#define LIGHT_SENSOR_PIN     34   // ADC light sensor

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

// ── System State ───────────────────────────────
struct DoorbellState {
  int           visitorCount;
  unsigned long lastRingTime;
  bool          motionDetected;
  bool          nightMode;
  bool          doorbellPressed;
  String        lastVisitorTime;
  int           lightLevel;
  bool          cameraActive;
  bool          doorLocked;
};
DoorbellState doorbell;

// ── Visitor Log ────────────────────────────────
struct VisitorLog { String timestamp; String event; bool notificationSent; };
VisitorLog visitorHistory[20];
int historyIndex = 0;

// ── Button Debounce ────────────────────────────
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 50;
int lastButtonState = HIGH;
int buttonState = HIGH;

// ── Timing ─────────────────────────────────────
unsigned long lastMotionCheck = 0, lastDisplayUpdate = 0;
const long motionCheckInterval = 500, displayUpdateInterval = 100;

// ── Melodies ───────────────────────────────────
const int melody1[]      = {523, 587, 659, 784};  // C D E G
const int noteDurations[] = {200, 200, 200, 400};

// ──────────────────────────────────────────────
// SETUP
// ──────────────────────────────────────────────
void setup() {
  Serial.begin(115200);
  pinMode(DOORBELL_BUTTON_PIN, INPUT_PULLUP);
  pinMode(PIR_SENSOR_PIN,      INPUT);
  pinMode(BUZZER_PIN,          OUTPUT);
  pinMode(RED_LED_PIN,         OUTPUT);
  pinMode(GREEN_LED_PIN,       OUTPUT);
  pinMode(BLUE_LED_PIN,        OUTPUT);
  pinMode(CAMERA_TRIGGER_PIN,  OUTPUT);
  pinMode(RELAY_DOOR_PIN,      OUTPUT);
  pinMode(LIGHT_SENSOR_PIN,    INPUT);

  // OLED Init
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("OLED failed"));
  } else {
    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(SSD1306_WHITE);
    display.setCursor(0, 0);
    display.println("SMART DOORBELL");
    display.println("Initializing...");
    display.display();
    delay(2000);
  }

  // State Init
  doorbell = {0, 0, false, false, false, "None", 0, false, true};
  digitalWrite(RELAY_DOOR_PIN, HIGH); // Start locked

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

  // Web Routes
  server.on("/",        handleRoot);
  server.on("/status",  handleStatus);
  server.on("/history", handleHistory);
  server.on("/ring",    handleTestRing);
  server.on("/unlock",  handleUnlock);
  server.on("/lock",    handleLock);
  server.on("/camera",  handleCamera);
  server.on("/clear",   handleClear);
  server.begin();
  Serial.println("✓ Web server started!");

  setRGBColor(0, 255, 0); playStartupChime(); delay(500);
  setRGBColor(0, 0, 0);
  updateDisplay();
}

// ──────────────────────────────────────────────
// LOOP
// ──────────────────────────────────────────────
void loop() {
  server.handleClient();
  unsigned long now = millis();
  checkDoorbellButton();
  if (now - lastMotionCheck >= motionCheckInterval) {
    lastMotionCheck = now;
    checkMotionSensor();
    checkLightLevel();
  }
  if (now - lastDisplayUpdate >= displayUpdateInterval) {
    lastDisplayUpdate = now;
    updateDisplay();
  }
}

// ── Button Debounce ────────────────────────────
void checkDoorbellButton() {
  int reading = digitalRead(DOORBELL_BUTTON_PIN);
  if (reading != lastButtonState) lastDebounceTime = millis();
  if ((millis() - lastDebounceTime) > debounceDelay) {
    if (reading != buttonState) {
      buttonState = reading;
      if (buttonState == LOW) handleDoorbellRing();
    }
  }
  lastButtonState = reading;
}

// ── Doorbell Ring Handler ──────────────────────
void handleDoorbellRing() {
  doorbell.doorbellPressed = true;
  doorbell.visitorCount++;
  doorbell.lastRingTime    = millis();
  doorbell.lastVisitorTime = getTimestamp();
  Serial.println("\n🔔 DOORBELL RANG! Visitor #" + String(doorbell.visitorCount));
  logVisitor("Doorbell Ring");
  setRGBColor(0, 0, 255);  // Blue
  activateCamera();
  playDoorbellChime();
  sendNotification("DOORBELL", "Someone is at your door!");
  flashDisplay("VISITOR AT DOOR!");
  delay(500);
  setRGBColor(0, 0, 0);
  doorbell.doorbellPressed = false;
}

// ── Motion Detection ───────────────────────────
void checkMotionSensor() {
  bool motion = digitalRead(PIR_SENSOR_PIN);
  if (motion && !doorbell.motionDetected) {
    doorbell.motionDetected = true;
    Serial.println("👁️ Motion detected at front door");
    logVisitor("Motion Detected");
    setRGBColor(255, 255, 0);
    if (doorbell.nightMode) setRGBColor(255, 255, 255);
    activateCamera();
    sendNotification("MOTION", "Motion detected at front door");
    delay(1000);
    setRGBColor(0, 0, 0);
  } else if (!motion && doorbell.motionDetected) {
    doorbell.motionDetected = false;
  }
}

// ── Light Level Check ──────────────────────────
void checkLightLevel() {
  doorbell.lightLevel = map(analogRead(LIGHT_SENSOR_PIN), 0, 4095, 0, 100);
  bool shouldBeNight = (doorbell.lightLevel < 30);
  if (shouldBeNight != doorbell.nightMode) {
    doorbell.nightMode = shouldBeNight;
    Serial.println(doorbell.nightMode ? "🌙 Night mode ON" : "☀️ Day mode ON");
  }
}

// ── Camera Activation ──────────────────────────
void activateCamera() {
  doorbell.cameraActive = true;
  digitalWrite(CAMERA_TRIGGER_PIN, HIGH);
  Serial.println("📷 Camera ON — visitor_" + String(doorbell.visitorCount) + ".jpg saved");
  delay(2000);
  digitalWrite(CAMERA_TRIGGER_PIN, LOW);
  doorbell.cameraActive = false;
}

// ── Doorbell Chime ─────────────────────────────
void playDoorbellChime() {
  for (int i = 0; i < 4; i++) {
    tone(BUZZER_PIN, melody1[i], noteDurations[i]);
    delay(noteDurations[i] + 50);
  }
  noTone(BUZZER_PIN);
}

void playStartupChime() {
  tone(BUZZER_PIN, 1000, 100); delay(150);
  tone(BUZZER_PIN, 1500, 100);
}

// ── RGB Helper ─────────────────────────────────
void setRGBColor(int r, int g, int b) {
  analogWrite(RED_LED_PIN,   r);
  analogWrite(GREEN_LED_PIN, g);
  analogWrite(BLUE_LED_PIN,  b);
}

// ── OLED Update ────────────────────────────────
void updateDisplay() {
  display.clearDisplay();
  display.setTextSize(1); display.setCursor(0, 0);
  display.println("SMART DOORBELL");
  display.drawLine(0, 9, 128, 9, SSD1306_WHITE);
  display.setCursor(0, 12); display.print("Visitors: "); display.println(doorbell.visitorCount);
  display.setCursor(0, 22); display.print("Last: ");     display.println(doorbell.lastVisitorTime);
  display.setCursor(0, 32); display.println(doorbell.motionDetected ? "MOTION DETECTED!" : "Motion: None");
  display.setCursor(0, 42); display.print("Door: ");     display.println(doorbell.doorLocked ? "LOCKED" : "UNLOCKED");
  display.setCursor(0, 52); display.print(doorbell.nightMode ? "Night Mode" : "Day Mode");
  if (doorbell.cameraActive) { display.setCursor(80, 52); display.print("CAM ON"); }
  display.display();
}

void flashDisplay(String msg) {
  for (int i = 0; i < 3; i++) {
    display.clearDisplay(); display.setTextSize(2); display.setCursor(0, 20);
    display.println(msg); display.display(); delay(500);
    display.clearDisplay(); display.display(); delay(300);
  }
}

// ── Notification Simulator ─────────────────────
void sendNotification(String type, String message) {
  Serial.println("\n📱 NOTIFICATION — " + type + ": " + message);
  Serial.println("   ✉️  Email: user@example.com");
  Serial.println("   📱 SMS: +1-234-567-8900");
}

void logVisitor(String event) {
  visitorHistory[historyIndex] = {getTimestamp(), event, true};
  historyIndex = (historyIndex + 1) % 20;
}

String getTimestamp() {
  unsigned long s = millis() / 1000;
  unsigned long m = s / 60, h = m / 60;
  s %= 60; m %= 60; h %= 24;
  String ts = (h < 10 ? "0" : "") + String(h) + ":";
  ts += (m < 10 ? "0" : "") + String(m) + ":";
  ts += (s < 10 ? "0" : "") + String(s);
  return ts;
}

// ── Web Handlers (abbreviated) ─────────────────
// Full handleRoot() generates the HTML dashboard.
// handleStatus()  → JSON status endpoint
// handleHistory() → JSON visitor log
// handleTestRing() / handleUnlock() / handleLock()
// handleCamera() / handleClear() — see full source above.
void handleStatus() {
  String json = "{";
  json += "\"visitors\":"  + String(doorbell.visitorCount)  + ",";
  json += "\"motion\":"    + String(doorbell.motionDetected ? "true":"false") + ",";
  json += "\"doorLocked\":"+ String(doorbell.doorLocked ? "true":"false")    + ",";
  json += "\"nightMode\":" + String(doorbell.nightMode ? "true":"false")    + ",";
  json += "\"lightLevel\":"+ String(doorbell.lightLevel) + "}";
  server.send(200, "application/json", json);
}
void handleTestRing()  { handleDoorbellRing(); server.sendHeader("Location","/"); server.send(303); }
void handleUnlock()    { doorbell.doorLocked=false; digitalWrite(RELAY_DOOR_PIN,LOW);  logVisitor("Door Unlocked"); setRGBColor(0,255,0); delay(500); setRGBColor(0,0,0); server.sendHeader("Location","/"); server.send(303); }
void handleLock()      { doorbell.doorLocked=true;  digitalWrite(RELAY_DOOR_PIN,HIGH); logVisitor("Door Locked");   setRGBColor(255,0,0); delay(500); setRGBColor(0,0,0); server.sendHeader("Location","/"); server.send(303); }
void handleCamera()    { activateCamera(); logVisitor("Camera Triggered"); server.sendHeader("Location","/"); server.send(303); }
void handleClear()     { doorbell.visitorCount=0; historyIndex=0; for(int i=0;i<20;i++) visitorHistory[i]={"","",false}; server.sendHeader("Location","/"); server.send(303); }
void handleRoot()      { /* Full HTML dashboard — see simulation for complete code */ server.send(200,"text/html","<h1>Smart Doorbell Online</h1>"); }
🟢 Ready Green LED — System online, no activity
🔵 Doorbell Blue LED — Doorbell pressed, chime plays
🟡 Motion Yellow LED — Motion detected by PIR
Night Light White LED — Night mode motion activated
1

🚀 Start the Simulation

Click the simulation link below, then press the ▶ Play button in Wokwi. Wait for "WiFi connected!" in the Serial Monitor.

2

🔔 Press the DOORBELL Button

Click the blue DOORBELL button. The buzzer plays a 4-note chime (C–D–E–G), the blue LED lights up, the camera LED activates, and the OLED flashes "VISITOR AT DOOR!". Check Serial Monitor for notification output.

3

👁️ Trigger Motion Detection

Click the PIR sensor in Wokwi and toggle it HIGH. The yellow LED activates, camera triggers, and a motion notification is sent. The visitor log updates automatically.

4

🌙 Test Night Mode

Rotate the "Light Sensor" potentiometer to a low value (below 30%). Night mode activates — the next motion detection will turn on the white LED (simulating an outdoor light).

5

🌐 Open the Web Dashboard

Find the IP address printed in the Serial Monitor (e.g., http://10.0.0.2). Open it in your browser to see the live dashboard with visitor stats, lock controls, and event log.

6

🔓 Remote Lock / Unlock Door

From the web dashboard, click "Unlock Door". The door lock LED turns off, a short tone plays, and the OLED updates to "UNLOCKED". Click "Lock Door" to re-secure.

7

📋 View Visitor Log

The web dashboard shows the 5 most recent events with timestamps. Click "Test Doorbell" to add entries from the browser. "Clear Log" resets the counter and history.


▶ Try it Live in Wokwi Simulator

No hardware needed — run the full doorbell simulation in your browser. Click the button, trigger motion, and watch the web dashboard in real time.

🔗 Open Free Simulation
💡
Pro Tip: After opening the simulation, check the Serial Monitor tab at the bottom for notification logs and IP addresses. The web dashboard opens in the Wokwi browser tab when you navigate to the ESP32's IP.

MakeMindz — Hands-on Electronics & IoT Tutorials

makemindz.com · ESP32 Project Series

Simulation powered by Wokwi

Comments

try for free