How to Build an ESP32-CAM Setup Portal Security Camera

A tiny camera that sets up its own Wi-Fi portal and can recover without rewriting code

ESP32SecurityIntermediate35 minutes4 components

Updated

How to Build an ESP32-CAM Setup Portal Security Camera
For illustrative purposes only
On this page

What you'll build

This guide turns an ESP32-CAM into a small security camera that behaves more like a finished gadget than a loose development board. On first boot it creates its own setup Wi-Fi network, serves a browser page for your home Wi-Fi details, then joins that network so you can open the camera locally. If you need to move it to a new network, the sketch includes a recovery path that returns it to setup mode without editing the firmware.

The build uses the AI Thinker ESP32-CAM, its onboard OV2640 camera, and the built-in flash LED. There is very little external wiring once the board is programmed, but the guide is careful about the annoying ESP32-CAM bits: stable 5V power, upload mode, serial monitor, and why the flash LED should stay off on boot.

The result is a useful workshop or desk camera you can flash from Schematik, configure from your phone, and keep improving later with motion alerts, a printed enclosure, or a better mounting bracket.

Wiring diagram

Wiring diagram

Interactive wiring diagram

Components needed

ComponentTypeQtyBuy
ESP32-CAM board with OV2640 cameraboard1
USB serial programmerother1
5V power supplyother1
Small camera enclosure or mountother1

Assembly

1

Prepare the ESP32-CAM

Fit the OV2640 camera ribbon firmly into the ESP32-CAM socket and mount the board so the antenna is not boxed in metal.

2

Connect the USB serial programmer

Wire programmer 5V to ESP32-CAM 5V, GND to GND, U0R to programmer TX, and U0T to programmer RX. Link IO0 to GND only while flashing.

3

Flash and open Serial Monitor

Upload the sketch, remove the IO0 to GND link, press reset, and watch Serial Monitor at 115200 baud.

4

Join the setup portal

On first boot, connect to the camera setup Wi-Fi network, open http://192.168.4.1, and save your 2.4 GHz Wi-Fi name and password.

5

Power and place the camera

Move to a stable 5V supply. Use the printed local IP address from Serial Monitor or the setup page to open the private camera stream.

Code

Arduino C++
#include <WiFi.h>
#include <WebServer.h>
#include <Preferences.h>
#include "esp_camera.h"

#define FLASH_LED_PIN 4
#define RESET_HOLD_SECONDS 8

// AI Thinker ESP32-CAM pin map
#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

Preferences prefs;
WebServer server(80);
String ssid;
String pass;

void startSetupPortal();
void startCameraServer();

bool configureCamera() {
  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_JPEG;
  config.frame_size = FRAMESIZE_VGA;
  config.jpeg_quality = 12;
  config.fb_count = psramFound() ? 2 : 1;
  config.fb_location = CAMERA_FB_IN_PSRAM;
  return esp_camera_init(&config) == ESP_OK;
}

void handleRoot() {
  server.send(200, "text/html", "<h1>ESP32-CAM</h1><p><a href='/capture'>Capture frame</a></p><p><a href='/reset'>Reset Wi-Fi setup</a></p>");
}

void handleCapture() {
  camera_fb_t *fb = esp_camera_fb_get();
  if (!fb) {
    server.send(503, "text/plain", "Camera capture failed");
    return;
  }
  server.sendHeader("Content-Disposition", "inline; filename=capture.jpg");
  server.send_P(200, "image/jpeg", (const char *)fb->buf, fb->len);
  esp_camera_fb_return(fb);
}

void setup() {
  pinMode(FLASH_LED_PIN, OUTPUT);
  digitalWrite(FLASH_LED_PIN, LOW);
  Serial.begin(115200);
  delay(200);

  prefs.begin("cam-portal", false);
  ssid = prefs.getString("ssid", "");
  pass = prefs.getString("pass", "");

  if (ssid.length() == 0) {
    startSetupPortal();
    return;
  }

  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid.c_str(), pass.c_str());
  Serial.print("Joining Wi-Fi");
  for (int i = 0; i < 40 && WiFi.status() != WL_CONNECTED; i++) {
    delay(250);
    Serial.print('.');
  }
  Serial.println();

  if (WiFi.status() != WL_CONNECTED || !configureCamera()) {
    startSetupPortal();
    return;
  }
  startCameraServer();
}

void startSetupPortal() {
  WiFi.mode(WIFI_AP);
  WiFi.softAP("ESP32-CAM-Setup");
  server.on("/", HTTP_GET, []() {
    server.send(200, "text/html", "<form method='post' action='/save'><label>Wi-Fi name <input name='s'></label><br><label>Password <input name='p' type='password'></label><br><button>Save</button></form>");
  });
  server.on("/save", HTTP_POST, []() {
    prefs.putString("ssid", server.arg("s"));
    prefs.putString("pass", server.arg("p"));
    server.send(200, "text/plain", "Saved. Rebooting into camera mode.");
    delay(500);
    ESP.restart();
  });
  server.begin();
  Serial.println("Setup portal: http://192.168.4.1");
}

void startCameraServer() {
  server.on("/", handleRoot);
  server.on("/capture", handleCapture);
  server.on("/reset", []() {
    prefs.clear();
    server.send(200, "text/plain", "Wi-Fi settings cleared. Rebooting.");
    delay(500);
    ESP.restart();
  });
  server.begin();
  Serial.print("Camera page: http://");
  Serial.println(WiFi.localIP());
}

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

// Run this and build other cool things at schematik.io
Libraries: ESP32 Arduino core

Ready to build this?

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

Open in Schematik →

Related guides