$7 ESP32 CAM example expanded

A proper implementation of a ESP32 CAM webserver; based on the Espressif example, but extended for real-world use

Similar projects worth following
This sketch is a extension/expansion/rework of the ‘official’ ESP32 Camera example sketch from Espressif:

It has extra wifi options, LED lamp control, permanent settings between reboots and a whole bunch of other little improvements, while keeping the same look and code structure as the original. The HTML is no longer stored as binary data in the header; but left plain-text for ease of modification.

Hopefully this expanded example is more useful for those users who wish to set up a simple ESP32 based webcam using the cheap(ish) modules freely available online. Especially the AI-THINKER board:

AI-THINKER ESP32-CAM and Other Modules:

I have four AI-THINKER ESP32-CAM boards, so the descriptions below are for that board. But I took care to leave the default definitions and controls for other boards in the example intact. You may need to adjust the programming method to suit the your board, look for examples online.

My Modifications:

Find my version of the code online at Github:

The original example has been extended to allow control of a high power LED FlashLamp, which is present on my modules. It can also blink a status LED to show when it connects to WiFi.

The Board model and WiFi details can be stored in a private config header file to allow easier updates, and some other defaults can be set there too. The lamp and status LED's are optional, and the lamp uses a exponential scale for brightness so that the control has some finess.

The compressed and binary encoded HTML used in the example has been unpacked to raw text, this makes it much easier to access and modify the Javascript and UI elements. Given the relatively small size of the index page there is very little benefit from compressing it.

I have left all the Face Recognition code untouched, it works, and with good lighting and camera position it can work quite well. But you can only use it in low-resolution modes, and it is not something I will be using.

The web UI has had minor changes to add the lamp control (only when enabled), I also made the 'Start Stream' and 'Snapshot' controls more prominent, and added feedback of the camera name + firmware.


  • I only have AI-THINKER modules with OV2640 camera installed; so I have only been able to test with this combination. I have attempted to preserve all the code for other boards and the OV3660 module, and I have merged all changes for the WebUI etc, but I cannot guarantee operation for these.
  • I created a small board with a handy switch for power, a pushbutton for the GPIO0 programming switch, and a socket for the AI-THINKER board. This proved very useful for development work and programming multiple devices.
  • I found some excellent cases on Thingieverse.


  • Improve Wifi, add a captive portal for setup and fallback, better disconnect/reconnect behaviour.
  • The module has a SD/TF card slot; this is currently unused, but I would like to add the ability to store snapshots; recording Video at low resolution may be possible, but the card interface is too slow for HD video as far as I know.
  • Remove face rcognition to save a Mb+ of code space and then implement over the air updates.
  • Combine current split html pages (one per camera type) into one which adapts as needed.

gzip - 2.36 MB - 03/14/2022 at 14:19


Zip Archive - 2.37 MB - 03/14/2022 at 14:19


Zip Archive - 2.13 MB - 03/14/2022 at 12:44


gzip - 2.12 MB - 03/14/2022 at 12:44


Zip Archive - 1.46 MB - 09/25/2020 at 21:04


View all 6 files

  • 1 × ESP32 CAM Lots of models available...
  • 1 × FTDI-Friend to program Or another way of programming the module. If you have a proper dev-module with a USB programming port you do not need this.
  • 1 × Hookup Wires, and possibly a case

  • V4.0 is out, with better support for budget modules.

    Owen03/14/2022 at 12:42 0 comments

    Actually, there are two new releases!

    • v3.5 with Face Recognition and a dependency on a older esp arduino core release (instructions in README) and config-settable xclk frequency.

    The main focus, however, is for v4.0

    • Latest esp-arduino-core release (v2.0.2 with SDK v4.4-beta1)
    • OTA enabled; configurable (including password) in myconfig.h
    • MDNS support
    • Camera bus xclock setting in config and UI, improves running on budget modules with bus issues, allows tuning the xclk frequency on the fly to tune for best speed/quality/etc
    • SPIFFS is now read (and formatted if needed) before camera module initialisation; this helps with a SPIFFS startup errors
    • 'kill stream' button in dump page, terminates the stream at the camera end in cases where the browser fails to drop the connection.
    • UI tuneable frame rate limiter for smoother displays and or lower network use
    • More info in dump page, including MCU temperature and xclk frequency
    • UI tweaks, some notes and help for quality, xclk and other settings
    • Improved display and logging of critical errors
    • Support ArduCAM_ESP32S_UNO
    • A whole bunch of spelling, comment and log tweaks

    For more; go over to the GitHub page ( where the README (and other documentation) is more extensive than here.

    Please report issues, look for help and discuss on GitHub: 

  • V4.0 Beta2, With OTA, but no face recognition

    Owen07/06/2021 at 10:37 0 comments

    I've got a new beta release out on Github for V4.0 of this project, The principal change is that I have removed all the face recognition code/UI, resulting in a much smaller codebase that allows for Over The Air updating (OTA). Super handy if you want to mount the camera in an inaccessible location.

    * The programming instructions have changed - The boards manager entry for the AI-Thinker board does not use OTA compatible partitioning, so you need to choose a Generai ESP32 dev board entry, select the correct partitioning, and enable PSRAM in the GUI. See the updated README

    * There are some other fixes for Video quality issues that should be useful

    * The OTA update can be secured with a password to stop the unit being hijacked

    * There is a fix to allow using the ESP Arduino IDE Core v2.x (currently in beta) and I have been testing with this for a few weeks now, no issues seen. If anything the performance and video quality are better.

  • Trivial release: 3.3

    Owen06/08/2021 at 06:42 0 comments

    I just did a (final?) release of the 3.x code to address a couple of trivial issues:

    * properly fix an old memory free()ing bug that affected face recognition. The temporary fix was fine but now we properly release the memory rather than keeping it assigned.

    * Disable all WiFi Powersaving to improve responsiveness

    There are no functional changes in this release.

  • V4 beta, dropped cheesy face recognition, added OTA

    Owen05/31/2021 at 14:47 0 comments

    On a new branch in the repo:

    Note that you need to select a different board than the usual 'AI-THINKER' one in the IDE for this since OTA uses a dfferent memory partitioning scheme. See the updated README on the branch for more.

  • 3.2 with a final Face Recognition fix

    Owen05/17/2021 at 11:34 0 comments

    I've done a V3.2 release

    This contains a small but significant bugfix that caused streams to crash if a still image capture occurred while face recognition is enabled. It is fixed by disabling face recognition on image captures while the stream is running.

  • V3.1 because... ESP

    Owen05/09/2021 at 16:23 0 comments

    This is a new minor release to incorporate fixes for the ESP 1.0.6 Arduino IDE version (which broke frame size selection).

    I also added some more detail about the number of served streams and images to the dump page, and improved the serial dump/debug behaviour so that you can request an info dump anytime, and it now works in AP mode too.

  • ESP-IDE 1.0.6 broke Framesizes

    Owen05/08/2021 at 16:03 0 comments

    ESP released an updated IDE (1.0.5, then 1.0.6), and while I had checked it did not break my build I had not realised that they added some extra resolutions to the camera, which in turn messed with the 'resolution' setting.

    I've now fixed this on the master branch in GitHub, and I'll bang a V3.1 release out asap with the fix.

    As a nice consequence the sketch has gained some extra modes, including a 96x96 thumbnail, HD-720p and (on 3MP camera equipped modules) FHD-1080p.

    NOTE: there is a ESP IDE V2 beta out, I have not tried compiling with this but I believe it will fail. I'll sort that out once it is out of beta, ESP are a bit erratic about this.

  • 3.0 is here

    Owen03/08/2021 at 20:18 0 comments

    It's about time I released something 'final', so here it is.

    I have a lot of possible plans for this, but other interests too. See my previous posts and GitHub for the changes that I want to make. Time permitting.

  • Picking this up again

    Owen01/23/2021 at 14:03 0 comments

    I needed to take a break from all things IT related, but since a new year is here I'm going to do a little more work on this project.

    Probably not too much, however.

    * A basic version with no face recog, but with OTA is my first step.

    * Multi-connection streams next.

    * I want to experiment with NTP and overlays; I think this is doable for all mode by overlaying while the frames are in the framebuffer, code to do this is already present and used for annotating face recognition. Fonts and Background hinting may be limited.

    * I might even move to a more coherent Wifi Framework as part of implementing OTA

    Finally; I'm waiting (patiently) on the 1.5 ESP IDF release for Arduino. It should be here soon..

  • V3 is nearly here, functionally complete. But facedb work dropped.

    Owen10/13/2020 at 14:33 0 comments

    I've uploaded the V3 release candidate; functionality complete. It may get some fixes, but my next task is to revamp the documentation and do the final release.

    I've decided to not do any development on the faceDB. This sketch is, and remains, a re-working of the Expressif example.

    In the esp example sketch the face recognition support is very basic; it does not use the 'named' face database, just a simple list. It only runs when the stream is running, and only works in low resolution modes. This is fine for an example and demo. But you need to look elsewhere for projects that really make use of the face recognition. One of my document tasks is to add a list of projects exploring the capabilities of these boards in more detail.


    I had to get real about saving the face database, eg it's not going to happen in this project without a proper library to work with. I spent a weekend of idle work on this, and decided I do not want to take the time needed to do it properly.

    The face database is actually a series of direct memory data structures created on the fly by the enrol functions; malloc()'d and then pointed to by a array of pointers. All very confusing, I got lost trying to work this all out in the source. I could relatively easily read the structs, but working out how to import and link everything would have taken too long.

    There is direct support in the esp face library for flashing the database to a memory partition.. which makes for permanent storage, **but** you need to provide a new partition map to the build system. Since I want to keep this project compatible with the ArduinoIDE I do not want to be telling people to hack their board definitions etc.. so this solution is rejected for now.

    - Maybe it could be done as an option for a platformio build. PR's on github are welcome.

View all 18 project logs

  • 1
    Setup, gather components

    For programming you will need a suitable development environment, I use the Arduino IDE, but this code should work in the Espressif development environment too.

    Follow This Guide to set up the Espressif Arduino core in the IDE.

    The AI-THINKER board requires use of an external 3.3v serial adapter to program; I use a FTDI Friend adapter, for more about this read AdaFruits excellent FTDI Friend guide.

    Be careful not to use a 5v serial adapter since this will damage the ESP32.

  • 2

    Hooking this up is pretty simple, see the diagram below. 


    Connect the RX line from the serial adapter to the TX pin on ESP32

    The adapters TX line goes to the ESP32 RX pin

    The GPIO0 pin of the ESP32 must be held LOW (to ground) when the unit is powered up to allow it to enter it's programming mode. This can be done with simple jumper cable connected at poweron, fitting a switch for this is useful if you will be reprogramming a lot.

    You must supply 5v to the ESP32 in order to power it during programming, the FTDI board can supply this.

  • 3

    Download the code from the Files section here or grab the latest release from Github at:

    • You can get the latest stable development release by cloning / downloading the master branch of the repo.

    This will give you an archive file with the Version number in it, Tou need to unpack this into your Arduino sketch folder, and then you need to rename the folder you just extracted to remove the version number, eg.esp32-cam-webserver-3.0 becomes esp32-cam-webserver.

    Once you have done that you can open the sketch in the IDE by going to the esp32-cam-webserver sketch folder and selecting esp32-cam-webserver.ino.

    In the sketch dont edit the main esp32-cam-webserver.ino sketch file, instead; copy the file myconfig.sample.h to myconfig.h and edit the settings there.

    You can set the camera board type, WiFi settings and some hardware/ui defaults in that file. Everything iscommented, and this file can remain private (it is in the .gitignore list).

View all 5 instructions

Enjoy this project?



Chris wrote 07/11/2021 at 10:24 point

Hey Owen, thanks for the really nice and well dokumented code! It works really well.

I got it working in no time and I'm really just a beginner at this.


PS I'm using the current 4.0 beta

  Are you sure? yes | no

Rick Vogel wrote 06/25/2021 at 02:59 point

I have not been able to get the 4.0 beta version working at all. I was able to program the module and got output, but unlike the 3.x version, I could not get the stream to work. I tried to configure as a static IP like I did with the 3.x version, but the serial output was not the same.  It seems to be stuck in AP mode.

  Are you sure? yes | no

nodemcu12ecanada wrote 05/09/2021 at 17:03 point

You can also use an Arduino Uno board to program it instead of the FTDI.

  Are you sure? yes | no

Owen wrote 05/09/2021 at 20:45 point

Thanks, That's a handy video. 

I have actually used my Uno as a icp programmer for another esp32 project, so I knew this was an option, but never looked into it since I had the FTDI lying around already. 

  Are you sure? yes | no

Gilles wrote 09/20/2020 at 09:57 point

Bonjour, quelqu'un a réussi à accéder à la vidéo de l'esp32cam depuis internet (pas depuis le réseau local)? 

Comment si Oui? 

En effet j'arrive bien depuis internet à afficher la page web de l'esp32, la capture d'image et les autres installations fonctionnent mais pas la vidéo ... Merci

  Are you sure? yes | no

Owen wrote 09/21/2020 at 10:14 point

"Hello, has anyone managed to access esp32cam video from internet (not from local network)?

How if yes?

Indeed I manage from the internet to display the web page of the esp32, the image capture and the other installations work but not the video ... Thank you."

- translated

I have not done this; and with the current non-ssl server and hacker nature of the code I cant really recommend it to anybody not expert enough to keep it secure.
But.. if you do understand the issues; the first point is that you need to expose both the Main UI port, and the stream port.

Or; just expose the stream port if you do not want to be able to access the UI. This gives you a 'pure', un-rotated and non-controllable video stream.

The best solution would be a proxy giving both encryption and http authentication; this is what I have tried to do but there is a major header length Issue that blocked me; and I lost interest since this is not a priority.

  Are you sure? yes | no

Gilles wrote 08/30/2020 at 18:01 point

Hello, I have the same concerns as Jassim Ali post of 04/18/20

 I can't get the video on esp 32 if I change the port (other than the port 80), I can control the flash, capture images but nothing on the video ...

 Anyone have an idea? 

Thank you

  Are you sure? yes | no

Owen wrote 09/21/2020 at 10:03 point

I just added a note to that question; in summary: v2.0 is out with more flexible networking options.

  Are you sure? yes | no

Arjen wrote 07/23/2020 at 09:40 point

Hello Owen,

Great job with this. Works a lot better than the original post with the Camera example.

I would like these in a 'door opening' event for security.

When a door opens (zwave module used in Domoticz) i want to record 5-10 seconds video and then post this to an email address.

Do you now a way to handle this situation?

Thanks for your time!! and your help in this. It is highly appreciated.

  Are you sure? yes | no

Owen wrote 08/20/2020 at 11:04 point

To do this within the ESP might be possible at low resolution, but you will need to look for people doing ESP32 security cam projects for that

Providing a fixed stream and a trigger, then recording that on something more capable like a Raspberry Pi is the way I'd be approaching it.

 It may be that someone already is doing similar. But it's beyond the scope of what I am trying to achieve here. I know that frameworks for connecting IOT devices into security systems exist, I've seen them in passing while researching other stuff.

  Are you sure? yes | no

Davor Dundovic wrote 08/30/2020 at 00:46 point

Hi Arjen!

I'm doing sort of security cam project.
So, my workflow is this:
(on ESP32-CAM)
- capture single image
- send it over WiFi using MQTT protocol to server (VPS)
(on server, Linux based)
- save image to mariaDB (mysql) database
- periodically run Python3 script that does motion detection using OpenCV
- you can also at this point through OpenCV add overlay data onto images (timestamp, motion status, rectangles that show what area triggered motion detection etc)
- when first motion is detected and some predefined time has passed with no motion after last, expand a bit image interval on both sides and generate ffmpeg concatenate input file
- run ffmpeg to stitch images into video clip, make time sync and save video file
- delete old/processed images

Adding email or other notifications should be trivial, server side, of course.


  Are you sure? yes | no

sanviernes wrote 07/18/2020 at 11:17 point

Great work!,  A simple question:  how to unpack to raw text the original camera_index_ov2640.h?  Thanks in advance

  Are you sure? yes | no

Albert wrote 07/23/2020 at 04:11 point

What do you mean? If you are using the Windows version, you should be able to see the contents of this file with Notepad. 

  Are you sure? yes | no

sanviernes wrote 07/29/2020 at 17:39 point

Oh no, I mean the original bin file from the original post with the Camera example

  Are you sure? yes | no

Owen wrote 08/20/2020 at 12:32 point

Reasonable question, sort of useful for diffs etc I guess.

I uploaded the unpacked originals to a new branch forked from the initial commit:


Unpacking is a bit tricky, there are online tools that can do this, but I brute-forced with a blob of C. I just uploaded that, see the readme on the branch.

  Are you sure? yes | no

Jassim Ali wrote 04/18/2020 at 02:37 point

first I would thank u for this amazing alternative, and I was looking for something like this urgently, I think the face recognition is useless on this cam, and I was looking to make an FPV robot that is connected to the WIFI and be able to access the stream remotely. it will be nice to make a (bare bone) version, that is light and require less computation, since the camera gets rely hot using the standard sketch. BTW, How can I change the defult server port (80) ? I tried so many lines but with no avail. all what I could do is change the lines in app_httpd file to this 

    config.server_port =***;
    config.ctrl_port = ***;

*** = my new port

this changed the streaming server but not the web server.

and If I entered the web server, I can take still pictures but not live stream. 

any suggestions ?


I added lines in the main code before the setup function to creat a static IP as :

IPAddress local_IP(192, 168, 8, 203);
IPAddress gateway(192, 168, 8, 1);

IPAddress subnet(255, 255, 255, 0);
IPAddress primaryDNS(192, 168, 8, 1); 
IPAddress secondaryDNS(192, 168, 8, 1); 

  Are you sure? yes | no

Owen wrote 08/30/2020 at 10:49 point

Sorry for delayed reply; A good question and something I definitely think should be user configurable.

I've added this to my immediate 'wish list':

  Are you sure? yes | no

Owen wrote 09/21/2020 at 10:02 point

I'm adding a reply here since thei is the original question:
The short answer is to upgrade to V2.0 or later; where you can set all the network options including static IP and specific port numbers for both the UI and the Stream.

The more complex answer is that proior to V2.0 the stream port number is calculated by the Javascript (running in the browser) as being 'my port number + 1', so if you force the stream onto another port in the app-server the javascript does not know this and still assumes the html port plus one.

V2.0 solves this by passing the stream port URL to the browser along with the other camera properties; and the javascript uses that value.

  Are you sure? yes | no

bentech4you wrote 04/04/2020 at 22:16 point


i would like to add overlay text(live temperate and humidity) to the video, is that possible?

  Are you sure? yes | no

Owen wrote 04/07/2020 at 21:37 point

Unfortunately this cannot be done directly in the stream output (for the same 'not enough cpu/ram' reasons that stops rotation etc).

But, as with the rotation setting, this could be applied in the browser window via an overlay pretty easily.  I have no plans to do anything about this myself, but if anybody wants to give it a go and submit a PR on GitHub I'd be interested.

  Are you sure? yes | no

Owen wrote 03/07/2021 at 13:05 point

A reply to myself; adding overlays is probably do-able while the image is in the framebuffer during streaming and adding an overlay at that time should be possible.  (this is much less memory/cpu intense than grabbing, rotating and re-saving). It's on the wish-list; but the whole project is n hiatus at present..

  Are you sure? yes | no

al wrote 01/21/2020 at 19:30 point


I tried your sketch on an ESP32-CAM from a Cinese vendor "DIY MORE" which looks identical to the AI-THINKER. I can see the camera dashboard in my browser but the serial monitor shows the following error...

"[E][camera.c:1344] esp_camera_fb_get(): Failed to get the frame on time! Camera capture failed"

Any idea as to what is causing the failure?


  Are you sure? yes | no

Owen wrote 04/07/2020 at 21:33 point

Answer no longer available due to the hackaday UI stupidity.. I mean, who thinks grey labels on black backgrounds enhance usability. Cos I think they make it really easy to hit the wrong button since they all look the same in sunlight.

IIRC the answer was memory. Or lack thereof.

  Are you sure? yes | no

Owen wrote 04/07/2020 at 22:13 point

I have now added a pins definition to the code (in pins.h) for non-psram modules that get this error when run with the standard examples.

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates