Building a DIY Desk Security Camera with ESP32-CAM: Complete Guide
Ever wanted a security camera that doesn't phone home to some cloud service in China? One that you actually own and control? For about $10 and an afternoon of tinkering, you can build exactly that.
The ESP32-CAM is one of the most capable microcontrollers for the price. It packs a dual-core processor, WiFi, 4MB of flash, and an OV2640 camera sensor into a package smaller than a credit card. Today we're turning it into a desk security camera with features that rival commercial products.
What We're Building
By the end of this guide, you'll have a camera that can:
- Stream live video to any browser (including iPhone Safari)
- Detect motion and send instant push notifications to your phone
- Record time-lapse photos to an SD card
- Save motion events automatically when movement is detected
- Control a flash LED for low-light situations
- Expose a JSON API for integration with other systems
All running on a $10 board with no monthly fees or cloud dependencies.
Hardware Requirements
| Component | Purpose | Approx. Cost |
|---|---|---|
| AI-Thinker ESP32-CAM | Main board with camera | $8-12 |
| ESP32-CAM-MB USB Dock | Programming interface | $3-5 |
| MicroSD Card (optional) | Photo storage | $5-10 |
| USB-A to Micro-USB Cable | Power and programming | $2 |
Total investment: Under $20 for a complete system.
You can find these on Amazon, AliExpress, or your favorite electronics supplier. The "AI-Thinker" variant is the most common and well-documented.
The ESP32-CAM Board
Before we dive into code, let's understand what we're working with:
┌─────────────────────────────────────────────────────────────┐
│ ESP32-CAM Board │
├─────────────────┬───────────────────┬───────────────────────┤
│ OV2640 │ ESP32 │ Flash LED │
│ Camera │ Processor │ (GPIO 4) │
│ Sensor │ + 4MB PSRAM │ │
└────────┬────────┴─────────┬─────────┴───────────────────────┘
│ │
Camera Ribbon MicroSD Slot
Cable (on back)Key specs:
- Processor: Dual-core Xtensa LX6 @ 240MHz
- Memory: 520KB SRAM + 4MB PSRAM (for high-res images)
- Camera: OV2640, up to 1600x1200 resolution
- WiFi: 802.11 b/g/n (2.4GHz only)
- Storage: MicroSD slot (up to 4GB officially, 16GB works fine)
- GPIO: Limited pins available (camera uses most of them)
Software Architecture
Our firmware has four main components working together:
1. Web Server (Port 80)
A simple HTTP server that handles:
- Dashboard at
/- Live stream with controls - MJPEG Stream at
/stream- Continuous video feed - Snapshots at
/capture- Single JPEG frames - API endpoints for toggling features
2. Motion Detection
Here's where it gets clever. Traditional motion detection requires decoding JPEG frames to raw pixels, which would crush the ESP32's limited resources. Instead, we use a byte-sampling algorithm:
// Sample every 100th byte of the JPEG data
for (size_t i = 0; i < frameLength; i += 100) {
int diff = abs(currentFrame[i] - previousFrame[i]);
if (diff > MOTION_THRESHOLD) {
changedBytes++;
}
}This approach works because JPEG compression is spatially consistent. If a region of the image changes significantly, the corresponding bytes in the JPEG stream will also change. We're essentially detecting compression differences rather than pixel differences.
It's not pixel-perfect, but it's remarkably effective for detecting people walking by or objects being moved. And it runs in microseconds instead of seconds.
3. Push Notifications
When motion is detected, we send an HTTP POST to ntfy.sh - a free, open-source push notification service. No accounts needed, no API keys, just a unique topic name.
HTTPClient http;
http.begin("https://ntfy.sh/your-unique-topic");
http.addHeader("Title", "Motion Detected!");
http.addHeader("Priority", "high");
http.addHeader("Tags", "warning,camera");
http.POST("Movement detected at your desk!");Install the ntfy app on your phone, subscribe to your topic, and you'll get instant notifications with a link to view the stream.
4. SD Card Recording
The ESP32-CAM has a MicroSD slot that we use for two recording modes:
- Motion Capture: Automatically saves a JPEG when motion is detected
- Time-Lapse: Captures frames at configurable intervals (default: 30 seconds)
Photos are organized into session folders with timestamps:
/sdcard/
├── session_20250122_090000/
│ ├── motion_20250122_091532.jpg
│ ├── motion_20250122_093217.jpg
│ ├── timelapse_20250122_090030.jpg
│ ├── timelapse_20250122_090100.jpg
│ └── ...Setting Up the Development Environment
We're using PlatformIO instead of the Arduino IDE. It's faster, has better dependency management, and integrates with VS Code.
1. Install PlatformIO
pip install platformio
# Or install the VS Code extension
# Search "PlatformIO IDE" in extensions2. Clone the Project
git clone https://github.com/cozyartz/esp32-cam.git
cd esp32-cam3. Configure Your Settings
Edit include/config.h:
// WiFi credentials
#define WIFI_SSID "YourNetworkName"
#define WIFI_PASSWORD "YourPassword"
// Push notifications - pick a unique random string
#define NTFY_TOPIC "mydeskcamera-a7x9k2"
#define NTFY_SERVER "https://ntfy.sh"
// Motion detection tuning
#define MOTION_THRESHOLD 15 // Sensitivity (0-255)
#define MOTION_PIXEL_COUNT 5000 // How many changed bytes trigger alert
#define MOTION_COOLDOWN_MS 30000 // 30 seconds between alerts
// Time-lapse interval
#define TIMELAPSE_INTERVAL_MS 30000 // 30 seconds
// Timezone for photo timestamps (EST = -18000)
#define TIMEZONE_OFFSET_SEC -180004. Build and Upload
Connect your ESP32-CAM to the USB dock, then:
# Build the firmware
pio run
# Upload to the board
pio run -t upload
# Monitor serial output
pio device monitorYou should see output like:
=== Desk Security Camera ===
Initializing camera...
PSRAM found, using high quality settings
Camera initialized!
Connecting to WiFi: YourNetwork
...
WiFi connected!
IP Address: 192.168.1.100
SD Card Type: SDHC
SD Card Size: 14920MB
Created session folder: /session_20250122_143052
SD card ready for recording!
========================================
Stream URL: http://192.168.1.100
Snapshot: http://192.168.1.100/capture
========================================Open that IP address in your browser, and you're live!
The Web Dashboard
The embedded dashboard provides everything you need in a clean, mobile-friendly interface:
Controls:
- Motion: ON/OFF - Toggle motion detection
- Flash: ON/OFF - Control the LED flash
- Timelapse: ON/OFF - Enable automatic time-lapse capture
- Save Motion: ON/OFF - Save photos when motion detected
- Take Snapshot - Download current frame
Status indicators:
- Motion detection state (armed/disabled)
- SD card availability
- Total photos saved this session
The dashboard polls /status every 5 seconds to keep everything in sync.
Creating Time-Lapse Videos
After your SD card has collected a few hundred (or thousand) photos, you can turn them into a video using FFmpeg:
# Copy photos from SD card to your computer
# Then run FFmpeg to create the video
# 30 fps time-lapse from all timelapse photos
ffmpeg -framerate 30 -pattern_type glob -i 'timelapse_*.jpg' \
-c:v libx264 -pix_fmt yuv420p timelapse.mp4
# Slower 10 fps for more detail
ffmpeg -framerate 10 -pattern_type glob -i 'timelapse_*.jpg' \
-c:v libx264 -pix_fmt yuv420p timelapse_slow.mp4This is perfect for monitoring plant growth, tracking desk activity over a workday, or watching weather changes.
Integrating with Apple Home (HomeKit)
If you're in the Apple ecosystem, you're probably wondering if this can work with the Home app. The short answer: partially.
The ESP32 doesn't have the processing power to encode H.264 video at the quality HomeKit requires (it needs 15-20 fps, the ESP32 tops out at 6-7 fps with native HomeKit). However, you have options:
Option 1: Homebridge (Full Camera Support)
Run Homebridge on a Raspberry Pi with the homebridge-camera-ffmpeg plugin. Point it at your ESP32-CAM's MJPEG stream, and the Pi handles the transcoding to HomeKit format.
Option 2: HomeSpan Motion Sensor (Lightweight)
Use the HomeSpan library to expose just the motion sensor to HomeKit. Your camera becomes a native HomeKit motion sensor without needing any bridge hardware. Notifications go straight to your iPhone.
We're working on a homekit branch with this integration.
API Reference
For those building integrations or automations:
| Endpoint | Method | Description |
|---|---|---|
/ |
GET | Web dashboard |
/stream |
GET | MJPEG video stream |
/capture |
GET | Single JPEG snapshot |
/motion?enable=0|1 |
GET | Toggle motion detection |
/flash?enable=0|1 |
GET | Toggle flash LED |
/timelapse?enable=0|1 |
GET | Toggle time-lapse mode |
/motioncapture?enable=0|1 |
GET | Toggle motion photo saving |
/photos |
GET | List saved photos (JSON) |
/photo?name=filename.jpg |
GET | Download specific photo |
/status |
GET | Full system status (JSON) |
Example status response:
{
"motion": true,
"flash": false,
"timelapse": true,
"motionCapture": true,
"sdCard": true,
"photoCount": 147,
"ip": "192.168.1.100",
"uptime": 86400,
"rssi": -62
}Troubleshooting
Camera won't initialize
- Check the ribbon cable - It's fragile and often comes loose during shipping
- Verify PSRAM - The AI-Thinker variant should have it; knockoffs sometimes don't
- Try lower resolution - Change
FRAME_SIZEtoFRAMESIZE_SVGAorFRAMESIZE_VGA
WiFi connection fails
- 2.4GHz only - ESP32 doesn't support 5GHz networks
- SSID/password - Double-check
config.h, no typos - Move closer - Start testing near your router
Stream is choppy
- One viewer at a time - The web server is single-threaded
- Reduce resolution - VGA (640x480) is the sweet spot
- Check WiFi signal -
/statusshows RSSI, aim for better than -70dBm
SD card not detected
- Format as FAT32 - exFAT won't work
- Capacity limit - Cards over 32GB may have issues
- Clean the slot - Dust is the enemy
No push notifications
- Check your topic - Must match exactly in
config.hand ntfy app - Internet access - ESP32 needs to reach ntfy.sh
- Cooldown period - Default 30 seconds between alerts
What's Next?
This camera is just the beginning. Here are some directions you could take it:
- Face detection using TensorFlow Lite on the ESP32
- MQTT integration for Home Assistant
- Deep sleep mode for battery-powered operation
- Pan/tilt mount with servo motors
- Multi-camera system with a central dashboard
The complete source code is on GitHub. Pull requests welcome!
The Bottom Line
For less than the cost of a fancy coffee, you can build a security camera that:
- Streams to any device with a browser
- Notifies you instantly when something moves
- Records photos locally with no cloud dependency
- Runs forever on USB power
No subscriptions. No accounts. No cloud. Just a tiny board doing exactly what you tell it to.
That's the beauty of DIY IoT done right.
This project is part of our hardware experiments series. Have questions or built something cool with the ESP32-CAM? Drop us a line at hello@techflunkylabs.com.
Build Your Financial Freedom While Building Cool Projects
Speaking of building things yourself - if you're the type who takes control of your tech instead of paying monthly subscriptions, you might appreciate taking the same approach with your finances.
While we're all about hardware hacks here, the same DIY mindset applies to building wealth. Check out this comprehensive forex trading course that teaches you to trade currencies with the same systematic, technical approach we use for engineering projects.
Sponsored content. TechFlunky Labs may receive compensation for purchases made through affiliate links.
Related Posts
Building Pink 9 to 5: E-Commerce for Breast Cancer Survivors
How we built a self-hosted Medusa.js e-commerce platform for a nonprofit supporting breast cancer survivors, using Docker, PostgreSQL, and Cloudflare Zero Trust.
Building BookAI: An AI-Powered Memoir Writing Platform
How I'm building a personal memoir writing platform using Astro, Cloudflare Workers AI, and D1 to document a life story that needs to be told.