How to Build an ESP32 Security Camera

Stream a tiny ESP32-CAM over your local Wi-Fi network

ESP32SecurityBeginner25 minutes4 components

Updated

How to Build an ESP32 Security Camera
For illustrative purposes only
On this page

What you'll build

This guide turns the tiny ESP32-CAM board from the Schematik Short into a local Wi-Fi security camera. The build uses an ESP32-CAM module, a matching USB programmer adapter, and a USB data cable, so there is no breadboard wiring for the basic version. The camera pins are already part of the ESP32-CAM board; the guide keeps those as board settings in code rather than loose jumper wires.

Once the sketch is deployed, the ESP32-CAM joins your 2.4 GHz Wi-Fi network and prints a local web address in the serial monitor. Open that address from a browser on the same network and you get a simple camera page that refreshes live captures from the board.

The code keeps the stream local and intentionally simple. It is a good desk, workshop, or enclosure prototype before you add authentication, a battery, motion detection, or a more permanent mount. This project came from Sam's Schematik Short, which you can watch on YouTube.

Wiring diagram

Wiring diagram

Interactive wiring diagram

Components needed

ComponentTypeQtyBuy
ESP32-CAM onboard camera modulesensor1
ESP32-CAM USB programmer adapterother1
USB cableother1
Small camera enclosure or mountother1

Assembly

1

Seat the ESP32-CAM on the USB adapter

Align the ESP32-CAM headers with the programmer adapter and press the board down evenly. Keep the camera ribbon seated and avoid touching the lens.

2

Connect USB and choose the ESP32-CAM board

Connect the adapter to your computer with a USB data cable. In Schematik or the Arduino tooling, use the AI Thinker ESP32-CAM style pinout unless your module says otherwise.

3

Add Wi-Fi details and deploy

Replace YOUR_WIFI_SSID and YOUR_WIFI_PASSWORD in the sketch, then deploy it to the ESP32-CAM. The code starts a small web server on your local network.

4

Open the local camera page

After the board restarts, read the IP address printed in serial output and open it in a browser on the same Wi-Fi network. The page refreshes camera captures to give you a simple live view.

Code

#include "esp_camera.h"
#include <WiFi.h>
#include <WebServer.h>

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

#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

WebServer server(80);

void handleRoot() {
  server.send(200, "text/html",
    "<!doctype html><html><head><meta name='viewport' content='width=device-width, initial-scale=1'>"
    "<title>ESP32 Security Camera</title>"
    "<style>body{font-family:sans-serif;background:#111;color:#fff;text-align:center;margin:0;padding:2rem}"
    "img{width:100%;max-width:640px;border-radius:16px}</style></head>"
    "<body><h1>ESP32 Security Camera</h1><img src='/capture' id='cam'>"
    "<script>setInterval(()=>{document.getElementById('cam').src='/capture?t='+Date.now()},700)</script>"
    "</body></html>"
  );
}

void handleCapture() {
  camera_fb_t *fb = esp_camera_fb_get();
  if (!fb) {
    server.send(500, "text/plain", "Camera capture failed");
    return;
  }

  server.sendHeader("Cache-Control", "no-store");
  server.send_P(200, "image/jpeg", (const char *)fb->buf, fb->len);
  esp_camera_fb_return(fb);
}

void setup() {
  Serial.begin(115200);
  Serial.setDebugOutput(false);
  delay(300);

  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_sccb_sda = SIOD_GPIO_NUM;
  config.pin_sccb_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_JPEG;
  config.frame_size = FRAMESIZE_VGA;
  config.jpeg_quality = 12;
  config.fb_count = 1;

  if (esp_camera_init(&config) != ESP_OK) {
    Serial.println("Camera init failed. Check the board type and camera ribbon.");
    return;
  }

  WiFi.begin(ssid, password);
  Serial.print("Connecting to Wi-Fi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  server.on("/", handleRoot);
  server.on("/capture", handleCapture);
  server.begin();

  Serial.println();
  Serial.println("Security camera ready");
  Serial.print("Open http://");
  Serial.println(WiFi.localIP());
}

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

// Run this and build other cool things at schematik.io
Libraries: esp32-camera

Ready to build this?

Open this project in Schematik to get the full wiring diagram, pin assignments, and deployable code for the ESP32 Security Camera.

Open in Schematik →

Related guides