Skip to Content

Stream Video with ESP32-WROVER CAM

Stream Video with ESP32-WROVER CAM

In this tutorial I will show you how to stream video from a Freenove ESP32-WROVER CAM board over your local Wi-Fi network to your Webbrowser. This means you can easily build your own wireless surveillance camera system that you can monitor from your computer.

If you haven’t used this board before, I suggest you read the Programming the ESP32-WROVER CAM tutorial first, which gets you started with the board.

Required Parts

You will need a Freenove ESP32-WROVER CAM board to follow this tutorial. Typically the board comes with extras such as an SD Card, an SD Card Reader and a USB-C cable, but in case that they are missing, I have linked them below as well.

ESP32-WROVER CAM

USB C Cable

SD Card Reader

MicroSD Card 16GB

Makerguides is a participant in affiliate advertising programs designed to provide a means for sites to earn advertising fees by linking to Amazon, AliExpress, Elecrow, and other sites. As an Affiliate we may earn from qualifying purchases.

Streaming Video

In this project we are going to implement a video streaming server that runs on the ESP32-WROVER. The Camera of the ESP32-WROVER takes images and serves them over Wi-Fi. The Wi-Fi router transports these images to the Web Browser on your PC, which displays the images as a video stream. The picture below illustrates the architecture of the system:

Streaming Video via local Wi-Fi
Streaming Video via local Wi-Fi

Note that the video stream is not encrypted or secure but will be only visible at an URL within your local Wi-Fi network, e.g. 192.168.2.40/stream. Only someone with access to your Wi-Fi network will be able to see the video stream.

Simple Code for a Video Streaming Server

The core for the ESP32-WROVER CAM comes with example code for a CameraWebServer that streams videos; see the Programming the ESP32-WROVER CAM tutorial for details. But that code is complex and the UI of the Webserver has many options.

That is great for trying things out but if you just want the plain video stream (e.g. for Home Assistant) and simple code that you can integrate with other functions, e.g. a motion activated video stream, then the following code is easier to use.

The simplification is largely due to the esp32cam library, which you can install it via the Library Manager in the Arduino IDE. Just search for “esp32cam” and press INSTALL. The picture below shows the completed installation:

esp32cam library installed via Library Manager
esp32cam library installed via Library Manager

Below you will find the complete code for the video streaming server, which uses the esp32cam library. Have a quick look first and then we will look at its details:

#include "WebServer.h"
#include "WiFi.h"
#include "esp32cam.h"

const char* WIFI_SSID = "SSID";
const char* WIFI_PASS = "PASSWORD";
const char* URL = "/stream";
const auto RESOLUTION = esp32cam::Resolution::find(800, 600);

WebServer server(80);

void handleStream() {
  static char head[128];
  WiFiClient client = server.client();

  server.sendContent("HTTP/1.1 200 OK\r\n"
                     "Content-Type: multipart/x-mixed-replace; "
                     "boundary=frame\r\n\r\n");

  while (client.connected()) {
    auto frame = esp32cam::capture();
    if (frame) {
      sprintf(head,
              "--frame\r\n"
              "Content-Type: image/jpeg\r\n"
              "Content-Length: %ul\r\n\r\n",
              frame->size());
      client.write(head, strlen(head));
      frame->writeTo(client);
      client.write("\r\n");
    }
  }
}

void initCamera() {
  using namespace esp32cam;
  Config cfg;
  cfg.setPins(pins::FreeNove);
  cfg.setResolution(RESOLUTION);
  cfg.setBufferCount(2);
  cfg.setJpeg(80);
  Camera.begin(cfg);
}

void initWifi() {
  WiFi.persistent(false);
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  while (WiFi.status() != WL_CONNECTED) {
    delay(100);
  }
  Serial.printf("Stream at: http://%s%s\n",
                WiFi.localIP().toString().c_str(), URL);
}

void initServer() {
  server.on(URL, handleStream);
  server.begin();
}

void setup() {
  Serial.begin(115200);
  initWifi();
  initCamera();
  initServer();
}

void loop() {
  server.handleClient();
}

Libraries

The sketch begins by including the necessary libraries:

#include "WebServer.h"
#include "WiFi.h"
#include "esp32cam.h"

These libraries enable HTTP server functionality (WebServer.h), Wi-Fi connectivity (WiFi.h), and camera control using the esp32cam library.

Constants

Next, we define constants for Wi-Fi and camera settings:

const char* WIFI_SSID = "SSID";
const char* WIFI_PASS = "PASSWORD";
const char* URL = "/stream";
const auto RESOLUTION = esp32cam::Resolution::find(800, 600);

The WIFI_SSID and WIFI_PASS variables store the credentials for the Wi-Fi network. You will have to replace SSID and PASSWORD with the actual login details for your Wi-Fi network.

The URL is the path on which the video stream will be served. You can change this to anything you like. For instance, “/video” or “/frontdoor”, just make sure you keep the slash (‘/’) at the beginning.

The RESOLUTION sets the desired resolution for the camera. Below is a list of the possible values for the camera resolution, though depending on the camera not all of them may work:

  • 96×96
  • 160×120
  • 128×128
  • 176×144
  • 240×176
  • 240×240
  • 320×240
  • 320×320
  • 400×296
  • 480×320
  • 640×480
  • 800×600
  • 1024×768
  • 1280×720
  • 1280×1024
  • 1600×1200

Objects

The following line creates the HTTP server object that listens on port 80. That is our web server that provides the video stream.

WebServer server(80);

handleStream

The function handleStream() handles the video stream whenever a client accesses the /stream path:

void handleStream() {
  static char head[128];
  WiFiClient client = server.client();

This line obtains the current client connected to the server. The response is then prepared:

server.sendContent("HTTP/1.1 200 OK\r\n"
                   "Content-Type: multipart/x-mixed-replace; "
                   "boundary=frame\r\n\r\n");

This header tells the browser that it will receive a multipart MJPEG stream, where each part is separated by a boundary called frame.

Inside the loop, the function first captures a frame from the camera:

  while (client.connected()) {
    auto frame = esp32cam::capture();

If a frame is successfully captured, a header is constructed describing the JPEG image, and sent to the client:

    if (frame) {
      sprintf(head,
              "--frame\r\n"
              "Content-Type: image/jpeg\r\n"
              "Content-Length: %ul\r\n\r\n",
              frame>size());
      client.write(head, strlen(head));
      frame->writeTo(client);
      client.write("\r\n");
    }

initCamera

The initCamera() function configures and starts the ESP32-WROVER camera:

void initCamera() {
  using namespace esp32cam;
  Config cfg;
  cfg.setPins(pins::FreeNove);
  cfg.setResolution(RESOLUTION);
  cfg.setBufferCount(2);
  cfg.setJpeg(80);
  Camera.begin(cfg);
}

First, a Config object is created. The setPins function configures the camera pins based on the FreeNove board. The ESP32-WROVER CAM is by FreeNove and that is what you need to set here. You can find the pin configurations of other supported boards here.

setResolution defines the resolution to use, while setBufferCount determines how many image buffers are used internally, which can help with faster framerates.

The setJpeg function sets the JPEG compression quality between 0 and 100. 80 is a good balance between quality and size. 90 gives you better quality but a smaller reduction in image size. For faster video you can reduce the resolution and the JPEG compression but obviously you will get smaller images with lower quality.

Finally, Camera.begin(cfg) initializes the camera with these settings.

initWifi

The initWifi() function connects the ESP32-WROVER board to the specified Wi-Fi network:

void initWifi() {
  WiFi.persistent(false);
  WiFi.mode(WIFI_STA);
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  while (WiFi.status() != WL_CONNECTED) {
    delay(100);
  }
  Serial.printf("Stream at: http://%s%s\n",
                WiFi.localIP().toString().c_str(), URL);
}

WiFi.persistent(false) disables writing the credentials to flash, which speeds up reconnection. The device is set to station mode (WIFI_STA), and it attempts to connect to the Wi-Fi using the given credentials.

Once connected, the IP address and stream path are printed to the serial monitor. You will need to copy and past this printer URL into your browsers address bar to see the video stream. The actual URL will depend on your ESP32-WROVER board but you can change the suffix “stream”. For my board, I see the following URL on the Serial Monitor:

Streaming URL printed to Serial Monitor
Streaming URL printed to Serial Monitor

Therefore, I copy “http://192.168.2.40/stream” into the address bar of my Chrome browser to access the video.

initServer

The initServer() function links the streaming path to the handler and starts the server:

void initServer() {
  server.on(URL, handleStream);
  server.begin();
}

This maps any request to /stream to the handleStream() function, and then starts the server.

setup

The setup() function starts the serial communication, connects to Wi-Fi, initializes the camera, and sets up the web server:

void setup() {
  Serial.begin(115200);
  initWifi();
  initCamera();
  initServer();
}

loop

Finally, the loop() function continuously handles incoming HTTP requests:

void loop() {
  server.handleClient();
}

This line allows the ESP32-WROVER to respond to client requests by invoking any registered handlers such as handleStream().

Altogether, this code creates a simple but effective MJPEG streaming server that can be accessed from any web browser within the same network, providing a live camera feed from the ESP32-WROVER CAM.

For downloading that code to your ESP32-WROVER CAM, select the ESP32 Wrover Module board and press the download button.

Select ESP32 Wrover Module as board
Select ESP32 Wrover Module as board

If you need help with this, have a look at our Programming the ESP32-WROVER CAM tutorial.

Conclusions

In this tutorial you learned how to stream video from an ESP32-WROVER CAM over your local Wi-Fi network to your Webbrowser.

The ESP32-WROVER CAM works great but is a rather big board. If need a board with a smaller footprint, I suggest the XIAO-ESP32-S3-Sense, which is tiny – a cube of about 20mm.

Also for object detection have a look at the Object Detection with ESP32-CAM and YOLO tutorial. It is written for the ESP32-CAM but requires only a minimal change (cfg.setPins(pins::FreeNove)) to work with the ESP32-WROVER CAM.

If you have any questions, feel free to leave them in the comment section.

Happy Tinkering ; )