DIY IP Camera with the Seeed XIAO ESP32 Sense, ESPHome, & Home Assistant
2026-03-10 | By Nate_Larson
License: Attribution Camera ESP32
Introduction
The Seeed Studio XIAO ESP32-S3 Sense is a compact, affordable development board with a built-in camera connector, onboard PSRAM, and Wi-Fi. When paired with ESPHome, it can become a low-cost IP camera integrated directly into Home Assistant — no cloud, no third-party app.
This guide walks through the complete process: flashing the ESP32 board with ESPHome, configuring the camera stream, routing that stream through go2RTC, and displaying it on a Home Assistant dashboard using the WebRTC Camera integration.
What You'll Need
Hardware
Seeed Studio XIAO ESP32-S3 Sense (with the OV2640/OV3660 camera module attached)
USB-C cable
A computer with a Chromium-based browser (Chrome, Edge) for the initial flash
Software / Services (all on your Home Assistant server)
Home Assistant OS (this guide uses HAOS on a Raspberry Pi 5)
ESPHome Builder app (formerly known as the ESPHome add-on)
HACS installed
WebRTC Camera integration by AlexxIT (search "WebRTC Camera" in HACS)
go2RTC app (installed via the Home Assistant App store)
Step 1 — Install the ESPHome Builder App
If you haven't already, install the ESPHome Builder app from the Home Assistant App store:
In Home Assistant, go to Settings → Apps → Install app.
Search for ESPHome and install the ESPHome Device Builder app.
Start it and enable Show in sidebar.
Step 2 — Create a New ESPHome Device
Open the ESPHome Builder app from the sidebar.
Click + New Device, select Empty Configuration, give it a name (e.g., Xiao Sense Cam).
Step 3 — Configure the YAML
Click Edit on your new device. Replace the entire contents with the configuration below.
Before pasting, read these important notes:
Generate your own API key: You can generate your key here (https://esphome.io/components/api/#configuration-variables), or run openssl rand -base64 32 in a terminal. Do not reuse someone else's key — it grants API access to your device.
Choose your own OTA password: Any string works; treat it like a password.
Choose your own fallback hotspot password: This is the Wi-Fi password for the board's emergency access point if it can't reach your network.
Set your own static IP, gateway, and subnet to match your network. Using a static IP ensures your go2RTC stream URL never changes.
secrets.yaml: Your Wi-Fi SSID and password should live in your ESPHome secrets.yaml file (ESPHome Builder app header→ "Secrets"), referenced as !secret wifi_ssid and !secret wifi_password.
esphome:
name: xiao_sense_cam
friendly_name: XIAO Sense Cam
esp32:
board: seeed_xiao_esp32s3
variant: esp32s3
framework:
type: esp-idf
psram:
mode: octal
speed: 80MHz
logger:
level: DEBUG
api:
encryption:
key: "YOUR_GENERATED_API_KEY_HERE"
reboot_timeout:
minutes: 5
ota:
- platform: esphome
password: "YOUR_OTA_PASSWORD_HERE"
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Reduce output power to improve connection stability.
# The default of 20dB is known to cause connectivity issues on this board.
# 8.5dB resolves most connection and dropout problems.
power_save_mode: none
output_power: 8.5dB
ap:
ssid: "XIAO Sense Cam Fallback Hotspot"
password: "YOUR_FALLBACK_HOTSPOT_PASSWORD_HERE"
manual_ip:
static_ip: 192.168.X.X # Set to your chosen static IP
gateway: 192.168.X.1 # Your router's IP
subnet: 255.255.255.0
captive_portal:
# Camera SCCB bus (I2C)
i2c:
id: bus_a
sda: GPIO40
scl: GPIO39
scan: true
esp32_camera:
name: XRP_POV
external_clock:
pin: GPIO10
frequency: 20MHz
i2c_id: bus_a
data_pins: [GPIO15, GPIO17, GPIO18, GPIO16, GPIO14, GPIO12, GPIO11, GPIO48]
vsync_pin: GPIO38
href_pin: GPIO47
pixel_clock_pin: GPIO13
# --- Performance & Stability ---
# 320x240 at 5fps is a conservative but reliable starting point.
# Increasing resolution or framerate demands more bandwidth and processing.
# If you push these values too high, you may see:
# - Dropped frames or a frozen stream
# - Unreliable Wi-Fi connectivity
# - The board running hot
# Increase incrementally and monitor with the temperature sensor below.
# Other resolution options and configuration details can be found here:
# https://esphome.io/components/api/#configuration-variables
resolution: 320x240
jpeg_quality: 30
frame_buffer_count: 2
max_framerate: 5 fps
vertical_flip: false
horizontal_mirror: false
# Expose an MJPEG stream on port 8080
esp32_camera_web_server:
- port: 8080
mode: stream
# --- Diagnostics & Monitoring ---
text_sensor:
- platform: wifi_info
ip_address:
name: "IP Address"
ssid:
name: "Connected SSID"
button:
- platform: restart
name: "Restart XIAO Sense Cam"
sensor:
- platform: wifi_signal
name: "WiFi Signal"
update_interval: 60s
- platform: uptime
name: "Uptime"
update_interval: 60s
# Monitor board temperature — useful if experimenting with higher resolutions/framerates
- platform: internal_temperature
name: "Sense Cam Temp"
binary_sensor:
- platform: status
name: "Status"
When you're done editing, click Save.
Step 4 — Flash the Firmware (First Time)
The first flash must be done over USB. After this, all future updates can be pushed wirelessly via OTA.
Plug the XIAO ESP32-S3 Sense into your computer via USB-C.
In the ESPHome app, click the three-dot menu on your device and select Install.
Choose Plug into this computer and follow the browser's prompts to select the board's serial port.
ESPHome's web-based flasher will compile and flash the firmware automatically.
Troubleshooting tip: If the flasher fails to connect, the board may need to be manually placed into bootloader mode. Hold the BOOT button on the board, press and release the RESET button, then release BOOT. This is not typically required when using ESPHome's web flasher, but it is worth trying if you encounter serial connection errors.
Once flashing completes, the board will reboot, connect to your Wi-Fi, and appear as online in the ESPHome dashboard. All future firmware changes can be deployed wirelessly using Install → Wirelessly.
At this point, you should have a camera feed that works locally within Home Assistant; however, this video feed would not render for users who were remotely connected to the server. The following steps were implemented to overcome this limitation.
Step 5 — Install the WebRTC Camera Integration via HACS
ESPHome exposes the camera as an MJPEG stream but does not create a native HA camera entity that supports smooth, low-latency playback. The WebRTC Camera integration bridges this gap cleanly.
Open HACS in Home Assistant.
Search for WebRTC Camera and install it.
Restart Home Assistant when prompted.
Go to Settings → Devices & Services → Add Integration, search for WebRTC Camera, and add it.
Step 6 — Configure go2RTC to Ingest the Camera Stream
go2RTC is a media server that pulls the MJPEG stream from the board and makes it available as an efficient, low-latency WebRTC stream for Home Assistant.
go2RTC is not available in the default app store repository, so you first need to add AlexxIT's add-on repository:
In Home Assistant, go to Settings → Add-ons → Add-on Store.
Click the three-dot menu (top right) and select Repositories.
Add the following URL and click Add: https://github.com/AlexxIT/hassio-addons
Close the dialog. The store will refresh, and go2RTC will now appear in the list.
Find go2RTC, install it, and start it.
With go2RTC running, open its web UI by clicking Open Web UI on the add-on page (or navigate to http://your-ha-ip:1984/ in your browser).
In the web UI, click Add to add a new stream.
Give your stream a name (e.g., xrp_pov) and enter your board's MJPEG stream URL as the source, replacing 192.168.X.X with your board's static IP: http://192.168.X.X:8080
Save the stream. It will now appear in the go2RTC stream list, and you can click on it to verify the feed is live before continuing.
The stream name xrp_pov is what you'll reference in the dashboard card. You can name it anything, but it must be consistent across this config and the card YAML.
Step 7 — Create a Generic Camera Entity in Home Assistant
With go2RTC running, create a Home Assistant camera entity that points to the local go2RTC stream.
Go to Settings → Devices & Services → Add Integration.
Search for and add the Generic Camera integration.
For the Still Image URL, enter: http://127.0.0.1:1984/api/frame.jpeg?src=xrp_pov
For the Stream Source URL, enter: rtsp://127.0.0.1:8554/xrp_pov
Complete the setup wizard. Home Assistant will now have a camera entity for your XIAO.
127.0.0.1 works here because go2RTC is running as an add-on on the same host as Home Assistant. Port 1984 is go2RTC's HTTP API (used for the still image snapshot), and port 8554 is its RTSP server (used for the stream source).
Step 8 — Add the Camera to Your Dashboard
Open the dashboard you want to add the camera to and enter edit mode.
Add a new card and choose Manual (to paste YAML directly).
Paste the following:
type: custom:webrtc-camera url: xrp_pov title: POV muted: true
Save the card. The live stream should appear within a few seconds.
The url value must match the stream name you defined in go2RTC. muted: true suppresses any audio (this board has a microphone, but it is not configured in this ESPHome setup).
A Note on Performance and Heat
The configuration above uses 320×240 at 5 fps deliberately. The XIAO ESP32-S3 Sense is a capable but small board, and higher resolutions or framerates place a significant load on both the processor and the Wi-Fi radio simultaneously. Pushing these settings too far can cause the following:
Dropped frames or a frozen/unreliable stream
Wi-Fi disconnections (the connectivity is already sensitive on this board, hence the reduced output_power)
The board is running noticeably warm
The board ships with small heatsinks; use them! The temperature sensor configured in the YAML will appear as an entity in Home Assistant, making it easy to monitor thermals while experimenting with settings. If you want higher quality video, increase resolution and jpeg_quality incrementally, check the temperature sensor and stream stability, and back off if you see problems. If the board gets uncomfortably hot under sustained use, consider adding a small active cooling solution (a 5V mini fan powered from the board's 5V pin, for example).
Troubleshooting
The board won't connect to Wi-Fi / keeps dropping. This is a well-documented issue with the XIAO ESP32-S3 Sense at the default ESPHome output power. Ensure output_power: 8.5dB is in your config. Also, ensure power_save_mode: none is set, and that the board's antenna is unobstructed.
The stream is frozen or very choppy. Lower resolution (try 240x176) and/or reduce max_framerate to 3 fps. Also, check your Wi-Fi signal strength using the WiFi Signal sensor entity.
ESPHome web flasher can't find the device / serial port. Try a different USB-C cable (some cables are charge-only). If the port is found but flashing fails, try manually entering bootloader mode: hold BOOT, tap RESET, release BOOT.
The go2RTC stream URL shows an error in the Generic Camera setup. Make sure the go2RTC add-on is running and that the stream name in the add-on config exactly matches the url value in the dashboard card and the Generic Camera URLs.
The camera entity shows "unavailable" in Home Assistant. Check that the board's IP address hasn't changed (confirm by looking at the "IP Address" sensor entity). If it has, either update your go2RTC config or revisit the static IP settings in the ESPHome YAML to lock it down permanently. For best results, the board should have a static IP address set in your WiFi router’s configuration.
Next Steps
Add the microphone: ESPHome supports the XIAO's digital microphone via the i2s_audio and microphone components, though streaming both audio and video simultaneously is demanding.
Increase resolution: Once your setup is stable, experiment with 640x480 and a slightly higher jpeg_quality. Monitor temperatures closely.
Automate restarts: The restart button entity can be called from an automation to recover a hung stream without physical access to the board.
Mounting: The XIAO's small form factor makes it easy to mount in tight spaces. The USB-C port is your power and (initial) programming connection, so plan cable routing accordingly.

