
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
Components needed
| Component | Type | Qty | Buy |
|---|---|---|---|
| ESP32-CAM onboard camera module | sensor | 1 | |
| ESP32-CAM USB programmer adapter | other | 1 | |
| USB cable | other | 1 | |
| Small camera enclosure or mount | other | 1 |
Assembly
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.
- The Short uses a tiny USB adapter, so you do not need loose jumper wires for this version.
- If your adapter has boot/upload buttons, leave them accessible while flashing.
- Unplug USB before reseating the ESP32-CAM on the adapter. Offset headers can short 5V into the wrong pin.
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.
- Open the serial monitor at 115200 baud so you can read the local IP address after upload.
- If no port appears, try another USB cable before changing code.
- The camera signal pins are built into the ESP32-CAM module. Do not add jumper wires for the camera itself.
- Some charge-only USB cables power the board but cannot upload sketches.
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.
- Use a 2.4 GHz Wi-Fi network. Most ESP32-CAM modules cannot join 5 GHz-only networks.
- Keep the board close to the router for the first test.
- Do not commit or share a version of the sketch with real Wi-Fi credentials in it.
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.
- If the page loads slowly, lower the frame size from FRAMESIZE_VGA to FRAMESIZE_QVGA.
- Use this as a local prototype, not an internet-exposed security system.
- Keep the camera stream on your private LAN unless you add authentication and HTTPS.
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.ioReady 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 →