Connect the ESP32-CAM to Home Assistant with ESPHome or an NVR
A single ESP32-CAM viewed in a browser tab is a gadget. Several of them inside Home Assistant, with snapshots on the dashboard, a flash switch, motion-based automations and recordings handled by network video recorder (NVR) software, start to look like a real home camera system, at a small fraction of the cost of commercial cameras.
This tutorial shows two routes. The first flashes the board with ESPHome, which talks to Home Assistant through its native API and needs no Arduino code. The second keeps your own Arduino streaming firmware and connects its MJPEG URL to Home Assistant or to NVR software such as Frigate, motionEye or Blue Iris.
Which Route Should You Choose?
| Route | How it connects | Strengths | Limits |
|---|---|---|---|
| ESPHome firmware | Native API, encrypted, auto-discovered | YAML only, OTA built in, easy to add sensors and the flash LED | Less control over the streaming code |
| Arduino firmware + MJPEG integration | Home Assistant polls the /stream and /jpg URLs | Reuse any custom sketch | No encryption on the local HTTP stream |
| Arduino or ESPHome + NVR | NVR pulls MJPEG and records it | Continuous recording, motion and object detection on a PC | Needs a server with storage and CPU |
Both routes depend on a stable board. Before connecting anything to Home Assistant, check that the camera streams reliably in a browser for an hour on its final power supply and in its final location.
Route 1: ESPHome Configuration
Install the ESPHome add-on (or the ESPHome command-line tool), create a new device and replace its YAML with the configuration below. The pin numbers are the same AI-Thinker map used by Arduino sketches: XCLK on GPIO 0, SCCB on GPIO 26 and 27, data Y2 to Y9 on GPIO 5, 18, 19, 21, 36, 39, 34 and 35, VSYNC 25, HREF 23, PCLK 22 and power-down on 32. Store passwords and keys in the secrets.yaml file.
esphome:
name: frontdoor-cam
esp32:
board: esp32cam
framework:
type: arduino
logger:
api:
encryption:
key: !secret api_key
ota:
- platform: esphome
password: !secret ota_password
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# AI-Thinker ESP32-CAM camera pins
esp32_camera:
name: Front Door Camera
external_clock:
pin: GPIO0
frequency: 20MHz
i2c_pins:
sda: GPIO26
scl: GPIO27
data_pins: [GPIO5, GPIO18, GPIO19, GPIO21, GPIO36, GPIO39, GPIO34, GPIO35]
vsync_pin: GPIO25
href_pin: GPIO23
pixel_clock_pin: GPIO22
power_down_pin: GPIO32
resolution: 800x600
jpeg_quality: 12
max_framerate: 10 fps # limit stream rate to Home Assistant
idle_framerate: 0.1 fps # snapshot rate when nobody is watching
# Optional: plain HTTP stream and snapshot for NVR software
esp32_camera_web_server:
- port: 8080
mode: stream
- port: 8081
mode: snapshot
# Flash LED on GPIO 4 as a dimmable light
output:
- platform: ledc
pin: GPIO4
channel: 2 # keep clear of the camera clock channel
id: flash_output
light:
- platform: monochromatic
name: Front Door Flash
output: flash_output
Connect the board with the USB-TTL adapter for the first install; after that ESPHome updates it over Wi-Fi. Home Assistant discovers the device, and after you enter the API encryption key the camera entity and flash light appear. ESPHome options change between releases, so if validation complains about a key, check the current esp32_camera documentation for your ESPHome version.
Route 2: MJPEG URLs in Home Assistant and NVRs
If you run your own Arduino firmware, such as the CameraWebServer example or a custom MJPEG server, the camera exposes two useful URLs: a stream such as http://192.168.1.60:81/stream and a still image such as http://192.168.1.60/capture or /jpg. Give the camera a fixed IP address with a DHCP reservation in your router so these URLs never change.
- Home Assistant: add the MJPEG IP Camera integration from Settings > Devices and Services, enter the stream URL as the MJPEG URL and the still URL as the still image URL.
- motionEye: add a network camera of type MJPEG with the stream URL; it then handles motion detection and recording.
- Frigate: Frigate is built around ffmpeg inputs. It can read an HTTP MJPEG input using ffmpeg input arguments for MJPEG sources; check the Frigate documentation for the MJPEG preset in your version, and keep detection at a low frame rate such as 5 fps.
- Blue Iris and similar Windows NVRs: add a generic MJPEG/HTTP camera with the stream path.
Example Automation: Snapshot on Doorbell Press
Once the camera entity exists, Home Assistant's camera.snapshot action can save an image whenever something happens. The YAML below saves a timestamped JPEG when a doorbell button entity turns on. Replace the entity IDs with your own and make sure the target folder is listed under allowlist_external_dirs in the Home Assistant configuration.
alias: Doorbell snapshot
triggers:
- trigger: state
entity_id: binary_sensor.doorbell_button
to: "on"
actions:
- action: light.turn_on
target:
entity_id: light.front_door_flash
data:
brightness_pct: 60
- delay: "00:00:01"
- action: camera.snapshot
target:
entity_id: camera.front_door_camera
data:
filename: "/config/www/doorbell/{{ now().strftime('%Y%m%d_%H%M%S') }}.jpg"
- action: light.turn_off
target:
entity_id: light.front_door_flash
Troubleshooting
- Camera entity unavailable after a while: weak Wi-Fi. Check the signal, fit an external antenna or add a mesh node nearby.
- ESPHome log shows camera init errors: a pin is wrong or the ribbon is loose; compare every pin with the AI-Thinker map.
- Dashboard image updates slowly: that is the idle frame rate at work; the full rate is used only while the stream is open.
- NVR records nothing: the stream is already in use by another client, or the URL lacks the port number.
Summary
ESPHome is the most integrated way to put an ESP32-CAM into Home Assistant: a short YAML file with the AI-Thinker pin map gives an encrypted camera entity, OTA updates and a dimmable flash. Custom Arduino firmware works too, through the MJPEG IP Camera integration or an NVR that records the stream. Plan for the board's one-stream limit, reserve fixed IP addresses, and use automations to take snapshots when events happen.