The Direct Answer: ESP32 RTSP Streaming in 2026
If you want to stream video via ESP32 RTSP (Real-Time Streaming Protocol) to an NVR, OBS, or VLC, you must use the ESP32-S3-WROOM-1 (N8R8 or N16R8) with 8MB of PSRAM. The original ESP32 WROOM lacks the memory bandwidth and dual-core throughput to sustain stable RTSP frame buffers without crashing the Wi-Fi stack. RTSP operates over TCP/UDP port 554, packaging the camera's JPEG or H.264 frames into a protocol that standard IP cameras use.
Target Board: ESP32-S3-WROOM-1 (N8R8 - 8MB Flash, 8MB PSRAM)
Camera Module: OV2640 (24-pin FPC) or OV5640
Difficulty: 3/5 (Requires IDE menu configuration for PSRAM)
Time to Build: 45 minutes
Core Libraries:
esp32-camera (v2.0.4+), Micro-RTSP (by geeksville)
Hardware Spec Sheet & Pin Mapping
The most common point of failure in ESP32 camera builds is a brownout caused by insufficient current. The OV2640 draws peak current during initialization, and the Wi-Fi radio draws peak current during transmission. If they overlap on a weak power rail, the board resets.
Required Parts
- MCU: Freenove ESP32-S3-WROOM CAM board or AI-Thinker ESP32-S3-CAM (Must have PSRAM).
- Sensor: OV2640 2MP Camera Module with 24-pin 0.5mm pitch FPC cable.
- Power Supply: 5V 2A (minimum) USB-C or barrel jack adapter. Do not rely on standard PC USB 2.0 ports (limited to 500mA).
- Heatsink: 10mm x 10mm adhesive aluminum heatsink for the ESP32-S3 chip (RTSP encoding runs the silicon hot).
ESP32-S3-CAM Pin Mapping (AI-Thinker / Freenove Standard)
These pin definitions map the ESP32-S3 GPIOs to the 24-pin camera FPC connector. You will paste these directly into the code block below.
| Camera Pin | ESP32-S3 GPIO | Camera Pin | ESP32-S3 GPIO |
|---|---|---|---|
| PWDN | -1 (Tied to RST) | D4 (Y4) | 4 |
| RESET | 21 | D3 (Y3) | 5 |
| XCLK | 11 | D2 (Y2) | 6 |
| SIOD (I2C Data) | 17 | D1 (Y1) | 7 |
| SIOC (I2C Clock) | 41 | D0 (Y0) | 15 |
| VSYNC | 8 | PCLK | 13 |
| HREF | 9 | D5 (Y5) | 16 |
| D9 (Y9) | 10 | D6 (Y6) | 14 |
| D8 (Y8) | 12 | D7 (Y7) | 40 |
Step-by-Step Build & Compilable Code
Before writing code, you must configure the Arduino IDE to utilize the board's PSRAM. If you skip this, the camera driver will fail to allocate frame buffers.
- Install Board Manager: Add the Espressif Systems URL to your Arduino IDE preferences and install the ESP32 Core (v2.0.14 or 3.0.x).
- Install Libraries: Open Library Manager and install
esp32-cameraby Espressif andMicro-RTSPby Kevin Hester. - Configure PSRAM: Go to Tools > PSRAM and select Enabled. Set Flash Size to 8MB and Partition Scheme to 8M with spiffs (3MB APP/9MB FATFS) or Huge APP (3MB No OTA).
- Upload the Code: Copy the sketch below, update your Wi-Fi credentials, and flash.
#include <WiFi.h>
#include "esp_camera.h"
#include "Micro-RTSP.h"
// --- NETWORK CREDENTIALS ---
const char* ssid = "YOUR_WIFI_SSID";
const char* password = "YOUR_WIFI_PASSWORD";
// --- ESP32-S3-CAM PIN DEFINITIONS ---
#define PWDN_GPIO_NUM -1
#define RESET_GPIO_NUM 21
#define XCLK_GPIO_NUM 11
#define SIOD_GPIO_NUM 17
#define SIOC_GPIO_NUM 41
#define Y9_GPIO_NUM 10
#define Y8_GPIO_NUM 12
#define Y7_GPIO_NUM 40
#define Y6_GPIO_NUM 14
#define Y5_GPIO_NUM 16
#define Y4_GPIO_NUM 4
#define Y3_GPIO_NUM 5
#define Y2_GPIO_NUM 6
#define Y1_GPIO_NUM 7
#define Y0_GPIO_NUM 15
#define VSYNC_GPIO_NUM 8
#define HREF_GPIO_NUM 9
#define PCLK_GPIO_NUM 13
// Custom Streamer Class for Micro-RTSP
class CamStreamer : public CStreamer {
public:
CamStreamer(u_short port) : CStreamer(port, 640, 480) {}
void stream() override {
camera_fb_t *fb = esp_camera_fb_get();
if (!fb) {
Serial.println("[ERROR] Camera capture failed during stream loop.");
return;
}
// Push JPEG frame buffer to RTSP clients
pushFrame(fb->buf, fb->len, millis());
esp_camera_fb_return(fb);
}
};
CamStreamer rtspStreamer(554);
void setupCamera() {
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; // 20MHz is stable for S3
config.pixel_format = PIXFORMAT_JPEG;
config.grab_mode = CAMERA_GRAB_LATEST;
config.fb_location = CAMERA_FB_IN_PSRAM;
if (psramFound()) {
config.frame_size = FRAMESIZE_VGA; // 640x480
config.jpeg_quality = 12;
config.fb_count = 2;
Serial.println("PSRAM found. Configuring high-res buffers.");
} else {
config.frame_size = FRAMESIZE_CIF; // Fallback if no PSRAM
config.jpeg_quality = 15;
config.fb_count = 1;
Serial.println("WARNING: No PSRAM detected. Stream will be low-res.");
}
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("[FATAL] Camera init failed with error 0x%x\n", err);
ESP.restart();
}
}
void setup() {
Serial.begin(115200);
while(!Serial); // Wait for serial monitor
WiFi.begin(ssid, password);
Serial.print("Connecting to WiFi");
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 30) {
delay(500);
Serial.print(".");
attempts++;
}
if (WiFi.status() != WL_CONNECTED) {
Serial.println("\n[FATAL] WiFi connection timed out.");
ESP.restart();
}
Serial.println("\nConnected! IP address: ");
Serial.println(WiFi.localIP());
setupCamera();
// Start RTSP Server
rtspStreamer.begin();
Serial.println("RTSP Server started on rtsp://" + WiFi.localIP().toString() + ":554/mjpeg/1");
}
void loop() {
rtspStreamer.handleRequests();
rtspStreamer.stream();
// Feed the watchdog to prevent core panic on heavy network load
yield();
}
Debugging: When the Stream Crashes or Fails to Connect
Camera and network stacks fighting for memory is the primary cause of ESP32 crashes. Here are the exact error strings you will see in the serial monitor, ranked by frequency, and how to fix them.
1. "camera init failed with error 0x20004" or "0x20001"
Cause: The camera driver failed to allocate memory for the frame buffer, or the I2C bus cannot communicate with the OV2640 sensor to configure it.
- Fix A (Memory): You forgot to enable PSRAM in the Arduino IDE Tools menu. Re-select the board, enable PSRAM, and reflash.
- Fix B (Hardware): The 24-pin FPC cable is not fully seated. The brown locking tab on the FPC connector must be flipped UP before inserting the cable, then pushed DOWN to lock. Clean the gold contacts with isopropyl alcohol.
2. "Guru Meditation Error: Core 1 panic'ed (LoadProhibited)"
Cause: A memory access violation. This happens when the Wi-Fi stack and the camera DMA (Direct Memory Access) controller attempt to write to the same PSRAM block simultaneously, or when the frame buffer overruns its allocated heap.
- Fix: Lower the XCLK frequency. In the code above,
config.xclk_freq_hz = 20000000;is safe. If you changed it to 24MHz for higher framerates, drop it back to 20MHz or 10MHz. Ensureconfig.grab_mode = CAMERA_GRAB_LATEST;is set so old frames are overwritten rather than queued in RAM.
3. VLC Connects but Shows "Buffering..." or a Gray Screen
Cause: Network throughput bottleneck or UDP packet loss. RTSP defaults to UDP, which drops frames silently on congested 2.4GHz Wi-Fi bands.
- Fix: In VLC, go to Tools > Preferences > Input / Codecs and change "Live555 stream transport" from RTP over RTSP (TCP) to UDP or vice versa. TCP forces reliable delivery but introduces latency; UDP drops frames but maintains real-time sync. Also, ensure your router isn't isolating Wi-Fi clients (AP Isolation).
When your ESP32 RTSP build fails on the bench, check these before rewriting code:
1. Is PSRAM explicitly enabled in the IDE Tools menu?
2. Are you using a dedicated 5V 2A power supply? (Laptop USB ports cause silent brownouts).
3. Is the ESP32 connected to a 2.4GHz Wi-Fi network? (ESP32 does not support 5GHz).
Extending and Simplifying Your RTSP Build
Depending on your end goal, raw RTSP might be overkill or insufficient. Here is how to pivot your architecture based on the ESP32-S3 hardware capabilities.
Simplifying: Drop RTSP for HTTP MJPEG
If you only need to view the stream in a standard web browser (Chrome/Safari) without installing VLC, RTSP is the wrong protocol. Browsers do not natively support RTSP. Simplify your build by using the WebServer.h library to serve a multipart/x-mixed-replace HTTP stream. This drops the TCP handshake overhead and renders directly in an <img> HTML tag, though it increases bandwidth usage by roughly 20%.
Extending: NVR Integration and PTZ Control
If you are integrating this into a home security stack like Frigate NVR or BlueIris, RTSP is mandatory. To extend the build:
- ONVIF Wrapper: NVRs use ONVIF to discover cameras and request PTZ movements. You can run a lightweight ONVIF daemon on a Raspberry Pi that proxies commands to your ESP32 via MQTT.
- Hardware PTZ: Add a PCA9685 I2C PWM servo driver to the ESP32-S3's I2C pins (GPIO 17/41) to pan and tilt the camera housing. Map the MQTT topics to the PCA9685 pulse widths.
Frequently Asked Questions (FAQ)
Can I use the original ESP32 WROOM for RTSP streaming?
Technically yes, but practically no. The original ESP32 WROOM (without the S3 designation and without external PSRAM) maxes out at QVGA (320x240) resolution for stable streaming. Attempting VGA (640x480) or higher will immediately exhaust the internal SRAM, causing a StoreProhibited panic. In 2026, the ESP32-S3 N8R8 is so inexpensive (around $6-$8 USD) that using an original WROOM for video is a false economy.
Why does my ESP32 RTSP stream lag by 2-3 seconds in VLC?
VLC defaults to a 1000ms (1 second) network caching buffer to prevent stuttering, and the ESP32's JPEG encoding adds another 200-500ms of latency per frame. To reduce this in VLC, open the "Open Network Stream" dialog, check "Show more options", and lower the "Network caching (ms)" value from 1000 to 200. Be aware that lowering this below 150ms will cause frame tearing if your Wi-Fi signal drops below -65dBm.
How do I view the ESP32 RTSP stream on my smartphone?
Native iOS and Android camera apps do not ingest RTSP URLs. You must use a third-party media player. Download VLC for Mobile or IP Cam Viewer. In VLC, navigate to the "Network" tab and paste your stream URL: rtsp://[YOUR_ESP32_IP]:554/mjpeg/1. Ensure your phone is connected to the same local 2.4GHz Wi-Fi network as the ESP32.
Does RTSP support audio on the ESP32-S3?
The Micro-RTSP library and the standard esp32-camera driver are strictly video-focused. While the ESP32-S3 has an I2S peripheral capable of reading from an INMP441 MEMS microphone, muxing audio and video into a single RTSP stream requires a container format like MP4 or MKV, which the ESP32 lacks the CPU cycles to package in real-time. If you need audio, you must run a separate, synchronized I2S audio stream over UDP or MQTT, or upgrade to an ESP32-P4 with a dedicated media pipeline.






