Full width home advertisement

Post Page Advertisement [Top]

Every "smart" doorbell on the market wants the same thing from you: your video feed, uploaded to their servers, wrapped in an $8-a-month subscription just to keep more than a few hours of history. I got tired of that trade after my package went missing from the porch and the "free tier" of my old doorbell had already overwritten the clip. So I built my own — an ESP32-CAM that runs a real AI model on the chip itself, detects people and packages in real time, and sends me a Telegram alert the moment something shows up. No cloud AI, no subscription, no footage leaving my house unless I want it to.

DIY AI Doorbell with ESP32-CAM

This guide walks through the whole build: training a tiny object-detection model with Edge Impulse, flashing it to an ESP32-CAM, wiring the alert hardware, and getting notifications on your phone.

📑 Table of Contents

    1. Why On-Device AI (TinyML) Instead of Cloud AI
    1. Components & Cost Breakdown
    1. Circuit Wiring Diagram
    1. Training the AI Model with Edge Impulse
    1. Deploying the Model to the ESP32-CAM
    1. Complete Code
    1. Setting Up Telegram Alerts
    1. Calibration Tips
    1. Troubleshooting
    1. Taking It Further

1. Why On-Device AI (TinyML) Instead of Cloud AI

Most "smart" cameras ship your video to a server, run detection there, and send a result back. That works, but it means a monthly bill, a privacy trade-off, and a camera that stops being smart the moment your internet drops.

TinyML flips that: the neural network runs directly on the microcontroller, inches from the sensor. Edge Impulse has made this genuinely approachable for hobbyists over the last couple of years — you don't need a data science background, just a phone camera to collect training images and a browser to train the model.

⚡ Why this matters in 2026: ESP32-S3 and even the classic ESP32-CAM now have enough RAM and processing headroom to run lightweight object-detection models (like FOMO — Faster Objects, More Objects) at several frames per second. Two years ago this was research-lab territory. Now it's a weekend build.

2. Components & Cost Breakdown

Component Model Purpose Price
Camera Module ESP32-CAM (AI-Thinker, OV2640) Capture + run inference $8
Programmer FTDI FT232RL USB-to-Serial Flashing the ESP32-CAM $3
Alert Active buzzer 5V Audible alert on detection $1
Status LED WS2812B addressable LED Visual status indicator $1
Power 5V 2A USB power adapter Stable power for camera + WiFi $4
Misc Jumper wires, mounting bracket Assembly $2

💰 Total Cost: ~$19 (vs. $130-250/year for commercial smart doorbells with subscriptions)

3. Circuit Wiring Diagram

The ESP32-CAM doesn't expose a lot of free GPIO pins because most are already committed to the camera interface, so wiring stays intentionally minimal.

Circuit Wiring Diagram

FTDI Programmer (temporary, for flashing only):

  • FTDI TX → ESP32-CAM U0R (RX)
  • FTDI RX → ESP32-CAM U0T (TX)
  • FTDI GND → ESP32-CAM GND
  • FTDI 5V → ESP32-CAM 5V
  • GPIO0 → GND (only while flashing — remove this jumper for normal operation)

Buzzer & LED (permanent):

  • Buzzer + → GPIO12
  • Buzzer − → GND
  • WS2812B DIN → GPIO13
  • WS2812B VCC → 5V
  • WS2812B GND → GND

⚠️ Important: The ESP32-CAM's onboard voltage regulator is a common weak point — it struggles to supply both the camera and WiFi radio reliably from a USB port's 500mA limit. Power it from a proper 5V/2A wall adapter, not directly from a laptop USB port, or you'll get random brownout resets during WiFi transmission.

4. Training the AI Model with Edge Impulse

This is the part that used to require a GPU cluster and now takes an afternoon.

  1. Create a free Edge Impulse account at edgeimpulse.com and start a new project.

  2. Collect training data. Use your phone (or the ESP32-CAM itself once it's flashed with a basic camera-streaming sketch) to capture 100-150 images each of: a person at your door, a package on the porch, and an "empty porch" baseline. Vary lighting, angle, and distance — the model generalizes far better with messy, realistic data than with perfectly staged photos.

  3. Label your images. Edge Impulse's built-in labeling tool lets you draw bounding boxes around "person" and "package" in each image directly in the browser.

  4. Choose FOMO as your learning block. FOMO (Faster Objects, More Objects) is a detection architecture specifically designed by Edge Impulse for microcontrollers — it trades some accuracy for a massive drop in RAM and inference time compared to standard object detection models like YOLO, which is exactly the trade-off an ESP32 needs.

  5. Train the model. Start with the default settings (96x96 grayscale input keeps things fast on this hardware) and let it run. Check the confusion matrix afterward — if "package" and "person" are getting confused with each other, add more varied training images for whichever class is weaker.

  6. Test in the browser using the "Live Classification" tab before deploying anything to hardware. This saves you from flashing a model that isn't actually ready yet.

5. Deploying the Model to the ESP32-CAM

Once you're happy with the model's accuracy:

  1. Go to the Deployment tab in Edge Impulse.
  2. Select Arduino library as the deployment target.
  3. Click Build — this packages your trained model into a .zip file formatted as an Arduino library.
  4. In the Arduino IDE: Sketch → Include Library → Add .ZIP Library, and select the file you just downloaded.
  5. Install the board support for ESP32 if you haven't already (Boards Manager → search "esp32" → install the espressif package).

6. Complete Code

#include <YOUR_PROJECT_NAME_inferencing.h>
#include "esp_camera.h"
#include <WiFi.h>
#include <FastLED.h>

// Camera pins for AI-Thinker ESP32-CAM
#define PWDN_GPIO_NUM     32
#define RESET_GPIO_NUM    -1
#define XCLK_GPIO_NUM      0
#define SIOD_GPIO_NUM     26
#define SIOC_GPIO_NUM     27
#define Y9_GPIO_NUM       35
#define Y8_GPIO_NUM       34
#define Y7_GPIO_NUM       39
#define Y6_GPIO_NUM       36
#define Y5_GPIO_NUM       21
#define Y4_GPIO_NUM       19
#define Y3_GPIO_NUM       18
#define Y2_GPIO_NUM        5
#define VSYNC_GPIO_NUM    25
#define HREF_GPIO_NUM     23
#define PCLK_GPIO_NUM     22

#define BUZZER_PIN 12
#define LED_PIN    13
#define NUM_LEDS   1

CRGB leds[NUM_LEDS];

const char* ssid = "YOUR_WIFI";
const char* password = "YOUR_PASSWORD";

void setupCamera() {
  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = Y2_GPIO_NUM;
  config.pin_d1 = Y3_GPIO_NUM;
  config.pin_d2 = Y4_GPIO_NUM;
  config.pin_d3 = Y5_GPIO_NUM;
  config.pin_d4 = Y6_GPIO_NUM;
  config.pin_d5 = Y7_GPIO_NUM;
  config.pin_d6 = Y8_GPIO_NUM;
  config.pin_d7 = Y9_GPIO_NUM;
  config.pin_xclk = XCLK_GPIO_NUM;
  config.pin_pclk = PCLK_GPIO_NUM;
  config.pin_vsync = VSYNC_GPIO_NUM;
  config.pin_href = HREF_GPIO_NUM;
  config.pin_sscb_sda = SIOD_GPIO_NUM;
  config.pin_sscb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_RGB565;
  config.frame_size = FRAMESIZE_96X96;
  config.fb_count = 1;

  esp_camera_init(&config);
}

void alertDetection(const char* label) {
  Serial.print("Detected: ");
  Serial.println(label);

  // Flash LED red and buzz
  leds[0] = CRGB::Red;
  FastLED.show();
  digitalWrite(BUZZER_PIN, HIGH);
  delay(300);
  digitalWrite(BUZZER_PIN, LOW);
  leds[0] = CRGB::Black;
  FastLED.show();
}

void setup() {
  Serial.begin(115200);
  pinMode(BUZZER_PIN, OUTPUT);
  FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS);

  setupCamera();

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

void loop() {
  camera_fb_t *fb = esp_camera_fb_get();
  if (!fb) {
    Serial.println("Camera capture failed");
    return;
  }

  ei::signal_t signal;
  signal.total_length = fb->len;
  // (Conversion of fb->buf into the format expected by
  // run_classifier() happens here — see the Edge Impulse
  // "esp32 camera" example sketch bundled with your exported
  // library for the full image-to-signal helper function.)

  ei_impulse_result_t result = { 0 };
  EI_IMPULSE_ERROR err = run_classifier(&signal, &result, false);

  if (err == EI_IMPULSE_OK) {
    for (size_t i = 0; i < result.bounding_boxes_count; i++) {
      ei_impulse_result_bounding_box_t bb = result.bounding_boxes[i];
      if (bb.value > 0.75) {  // confidence threshold
        alertDetection(bb.label);
      }
    }
  }

  esp_camera_fb_return(fb);
  delay(2000); // check every 2 seconds
}

Note: The image-to-signal conversion step is intentionally left as a comment above — Edge Impulse's exported library includes a ready-made esp32_camera example sketch with this helper already written for you. Start from that example and drop in the alert/buzzer/LED logic shown here rather than writing the conversion function from scratch; it saves a lot of debugging.

7. Setting Up Telegram Alerts

A buzzer is great when you're home, useless when you're not. Add a phone notification with the UniversalTelegramBot library:

  1. Message @BotFather on Telegram and create a new bot to get an API token.
  2. Message @userinfobot to get your personal chat ID.
  3. Install the UniversalTelegramBot and ArduinoJson libraries via the Library Manager.
  4. Inside alertDetection(), add a bot.sendMessage(CHAT_ID, message, "") call using your token and chat ID.

Now instead of (or alongside) the buzzer, you get a push notification with the detection label straight to your phone, anywhere you have signal.

8. Calibration Tips

  1. Confidence threshold: Start at 0.75 and adjust based on false positives. A shadow triggering a "person" alert at 3am gets old fast — raise the threshold if that happens.
  2. Lighting: FOMO models trained mostly on daytime images will struggle at night. If your porch has night-time traffic, collect and label some low-light training images too, or add an IR illuminator.
  3. Mounting angle: Keep the camera at a consistent height and angle to what you trained on — a model trained on chest-height shots performs worse on a camera mounted looking sharply downward.

9. Troubleshooting

Camera fails to initialize:

  • Double-check the PSRAM is enabled in Arduino IDE (Tools → PSRAM → Enabled) — the AI-Thinker board requires it for camera buffering.
  • Reseat the ribbon cable connecting the OV2640 sensor to the board; it's a common loose-connection point.

Board won't upload / gets stuck at "Connecting...":

  • Confirm GPIO0 is jumpered to GND before pressing the reset button, and remove that jumper after flashing.
  • Some FTDI adapters default to 5V logic — make sure yours is set to 3.3V, or you risk damaging the board.

Random resets during WiFi activity:

  • This is almost always power-related. Swap to a proper 5V/2A supply and add a 470µF capacitor across the 5V and GND pins close to the board.

Model runs but never detects anything:

  • Verify your confidence threshold isn't set unreasonably high.
  • Re-check that the image resolution and color format in your sketch (96x96, RGB565) match what you selected when training in Edge Impulse — a mismatch here silently produces garbage predictions.

10. Taking It Further

Once the base build works reliably, a few upgrades are worth the extra weekend: adding a second detection class for specific delivery van logos, triggering a smart plug to turn on a porch light automatically when "person" is detected after dark, or logging detections with timestamps to an SD card for a simple local history — no cloud storage required.

Final Thoughts

This project is a good reminder that "AI" doesn't have to mean "someone else's server." Running the model directly on an $8 camera module, with zero recurring cost and zero footage leaving your house, is genuinely more private and more reliable than most commercial alternatives — and once you've trained one FOMO model, retraining it for a completely different detection task (a pet, a specific tool on a workbench, a parking spot) is just a matter of swapping the training images.

If you build this, share your confusion matrix and detection results — I'd love to see what accuracy other people are getting with different porch lighting setups.


Related Projects You'll Love 

Bottom Ad [Post Page]