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 into a local Wi-Fi security camera. The build targets the AI Thinker ESP32-CAM specifically: the OV2640 camera is built into the module with its signal pins fixed on the PCB, so there is no camera breakout to wire. The only external hardware is the matching USB programmer adapter and a data cable.

Once the sketch is deployed, the board joins your 2.4 GHz Wi-Fi network and prints a local address in Serial Monitor. Open that address from any browser on the same network and you get a simple page that refreshes live captures from the camera every 700 milliseconds.

The stream stays local and the code stays simple. It is a practical starting point before adding authentication, motion detection, a battery, or a more permanent mount. This project came from Sam's Schematik Short, which you can watch on YouTube.

What you are building

This guide covers a local-network camera only. The firmware has four jobs:

  1. initialise the OV2640 with the AI Thinker pin map,
  2. connect to your 2.4 GHz Wi-Fi network using YOUR_WIFI_SSID and YOUR_WIFI_PASSWORD,
  3. serve a root page that auto-refreshes captures at /capture every 700 ms,
  4. return JPEG frames from the camera buffer on each request to /capture.

Push notification, authentication, cloud storage, and motion detection are out of scope.

Upload and calibrate

Open the sketch in Schematik and set the two Wi-Fi placeholders before deploying:

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

Deploy from the Schematik project page, then open Serial Monitor at 115200 baud. After the board connects you will see:

Security camera ready
Open http://192.168.x.x

Open that address in a browser on the same 2.4 GHz network. The page loads a JPEG image that refreshes automatically. If the frame rate feels slow, change FRAMESIZE_VGA to FRAMESIZE_QVGA in the camera config for a faster but smaller image.

Do not commit or share the sketch with real Wi-Fi credentials in it.

Troubleshooting

  • "Camera init failed" in Serial Monitor. The board type is probably wrong. Select the AI Thinker ESP32-CAM profile, not a generic ESP32 DevKit. Also check that the camera ribbon is fully clipped into the socket.
  • No serial port appears after connecting USB. The cable is likely charge-only. Swap it for a cable that transfers data — the port should appear in the OS device list immediately.
  • Board does not enter upload mode. Make sure IO0 is connected to GND before pressing upload, then remove the link before resetting.
  • ESP32-CAM resets or drops connection during capture. The USB supply may not be stable under camera load. Try a powered hub or a dedicated 5 V adapter, keeping the ground shared.
  • Browser page loads but image is black or corrupted. Power-cycle the board. The OV2640 occasionally needs a clean reset to initialise correctly after a flash.
  • "Connection refused" when opening the IP address. The board has not finished connecting to Wi-Fi. Wait a few seconds and refresh; Serial Monitor will show the IP once the server starts.

Going further

Once the basic stream is reliable, the natural next step is to add a motion-detection pass in the capture loop: compare consecutive JPEG buffers and trigger an alert when enough pixels change. Pair that with an MQTT publish or a simple HTTP webhook call and you have a basic event-driven camera without any external cloud dependency.

For a version that sets itself up without editing credentials and supports recovery without reflashing, see the ESP32-CAM Setup Portal Security Camera guide, which adds a browser-based Wi-Fi configuration page and a power-cycle reset path.

Wiring diagram

Loading diagram…
Interactive wiring diagram

Components needed

Supplier links, prices, and availability are shown as a guide and may change. Schematik may earn a commission from purchases made through affiliate links.

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.

Pin assignments

PinConnectionType
GPIO 0esp32-cam-camera-1 XCLKDIGITAL
GPIO 26esp32-cam-camera-1 SIODI2C
GPIO 27esp32-cam-camera-1 SIOCI2C
GPIO 5esp32-cam-camera-1 Y2DATA
GPIO 18esp32-cam-camera-1 Y3DATA
GPIO 19esp32-cam-camera-1 Y4DATA
GPIO 21esp32-cam-camera-1 Y5DATA
GPIO 36esp32-cam-camera-1 Y6DATA
GPIO 39esp32-cam-camera-1 Y7DATA
GPIO 34esp32-cam-camera-1 Y8DATA
GPIO 35esp32-cam-camera-1 Y9DATA
GPIO 25esp32-cam-camera-1 VSYNCDATA
GPIO 23esp32-cam-camera-1 HREFDATA
GPIO 22esp32-cam-camera-1 PCLKDATA
GPIO 32esp32-cam-camera-1 PWDNDIGITAL
3V3esp32-cam-camera-1 3V3POWER
GNDesp32-cam-camera-1 GNDGROUND
5Vesp32-cam-usb-programmer-1 5VPOWER
GNDesp32-cam-usb-programmer-1 GNDGROUND
GPIO 3esp32-cam-usb-programmer-1 TXUART
GPIO 1esp32-cam-usb-programmer-1 RXUART
GPIO 0esp32-cam-usb-programmer-1 IO0_FLASH_LINKDIGITAL

Code

Arduino C++
#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