Open-source project
s60sc/ESP32-CAM_MJPEG2SD avatar
s60sc/ESP32-CAM_MJPEG2SD

s60sc/ESP32-CAM_MJPEG2SD: motion capture on an ESP32 camera, written to AVI

ESP32 Camera motion capture application to record JPEGs to SD card as AVI files and stream to browser as MJPEG. If a microphone is installed then a WAV file is also created. Files can be uploaded via FTP or downloaded to browser.

1,740 stars368 forksC++AGPL-3.0

At a glance

What is it?
ESP32-CAM_MJPEG2SD turns an ESP32 or ESP32S3 camera board into a motion-triggered or continuous recorder that writes JPEGs into AVI files on an SD card and streams MJPEG to a browser. It is a large Arduino sketch with a long feature list and a real heap ceiling.
Who is it for?
Adopt it if you have an ESP32S3 camera board, a genuine SD card and the patience to configure one header file, and you want recordings that land on local storage rather than in a cloud account. Do not adopt it if you need a low-power battery node, a stable long-term API, or a build you can debug without reading a large single-repo sketch.
Can I use it commercially?
Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
Is it still maintained?
Yes. The repository last received commits 6 days ago.
What is it written in?
Mainly C++, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 1, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What ESP32-CAM_MJPEG2SD records, and for whom

The project exists to answer a narrow question: how do you get usable video out of a board with a few megabytes of PSRAM and no operating system? The README states the purpose directly: video capture of motion detection or continuous recording, with security cameras, wildlife monitoring, rocket flight monitoring and FPV vehicle control given as examples. The author's argument for the AVI container is storage efficiency, not playback elegance. Saving a set of JPEGs as a single file is faster than saving them individually and easier to manage, particularly at small image sizes.

That framing tells you who this is for. It suits someone who already has an ESP32-CAM or a Freenove ESP32S3 Cam, a microSD card, and a reason to keep footage locally instead of paying a subscription. It suits a maker who wants a camera that also drives servos, reads a BMP280 or an MPU9250, and publishes to MQTT for Home Assistant. It does not suit someone who wants a camera that works after ten minutes of setup. The README itself warns that this is a complex app, and asks users to raise issues only for actual bugs (ERR messages, unhandled library errors or crashes) while directing enhancement suggestions to Discussions. That is an unusually blunt maintenance policy, and it is worth reading as a signal about the support you should expect.

How the frame buffer and AVI writer actually work

The design section describes a pipeline built around PSRAM. The ESP32 Cam module has 4MB of PSRAM, 8MB on most ESP32S3 boards, and that memory buffers camera frames and the construction of the AVI file. The stated goal is to minimise the number of SD file writes and to align writes with the SD card sector size. Playback runs the same path in reverse: the AVI is read from SD into a multiple-sector buffer and sent to the browser as timed individual frames.

File naming is deterministic and worth knowing before you fill a card. Files use a date time format YYYYMMDD_HHMMSS with frame size, recording rate and duration appended, for example 20200130_201015_VGA_15_60.avi, and they live in a per-day folder named YYYYMMDD. A trailing _S marks a file that contains audio, and _M marks one with telemetry. Time comes from an NTP server or from a connected browser client, which means a camera that never reaches the network may not produce correctly named files.

The SD bus choice is a real trade-off rather than a default worth ignoring. The README says the card is used in MMC 1 line mode by default because it is practically as fast as MMC 4 line mode on the ESP32 and frees pin 4, connected to the onboard lamp, and pin 12, which can carry a PIR. On the ESP32S3, tests by a contributor named in the README indicate that MMC 4 line mode doubles speed. So the default protects two pins at a cost that only appears on the newer chip.

Installing ESP32-CAM_MJPEG2SD in the Arduino IDE

There is no package manager step and no binary release. The README says to download the GitHub files into the Arduino IDE sketch folder, removing -master from the application folder name. You then compile with at least arduino-esp32 core v3.1.1, which the README says contains network fixes and frame selection changes.

Board selection happens in a header file, not in a menu. Uncomment exactly one CAMERA_MODEL_* define in ESP32-CAM_MJPEG2SD.h unless you are using one of the defaults.

cpp
// ESP32-CAM_MJPEG2SD.h
// ESP32 Cam board
#define CAMERA_MODEL_AI_THINKER
// Freenove ESP32S3 Cam board
// #define CAMERA_MODEL_FREENOVE_ESP32S3_CAM

Optional features are off by default. To include one, set the relevant INCLUDE_* define to true in the same header.

cpp
// ESP32-CAM_MJPEG2SD.h
#define INCLUDE_MQTT true
#define INCLUDE_FTP true

In the IDE, select the ESP32 or ESP32S3 Dev Module board, enable PSRAM, and pick a partition scheme. The README lists Minimal SPIFFS for the ESP32 and either 8M with spiffs or 16MB(3MB APP...) for the ESP32S3. After flashing, the camera brings up a configuration web page where you set network details; the README notes that Ethernet can be selected instead of WiFi, which is where the Waveshare ESP32-S3-ETH pins and the external W5500 controller definitions come in. The first thing you should see is that page, not a video stream.

Frame rate is the constraint that decides your use case

The README publishes a table of achieved recording rates on a freshly formatted Sandisk 4GB SDHC Class 2 card in an AI Thinker OV2640 board, at maximum JPEG quality and a 20MHz clock. The numbers are the most useful thing in the repository for planning, because they show where the application stops being a video recorder and becomes a stills camera.

At small frame sizes the gap to the camera's own maximum is modest: 45 fps against 50 for 96X96 through 240X240, and 40 fps against 50 for QVGA through HVGA. At VGA the camera itself drops to 25 fps and the application records 20. From XGA upward the application records 5 fps while the camera can do 12.5, and UXGA sits at 5 fps with a 450ms detection time. The README adds that on ESP32S3 with a 24MHz clock, maximum frame rates can rise from 50 to 60 and from 25 to 30, though JPEG quality may need to drop. It also states that the ESP32S3 runs the app about double the speed of the ESP32, mainly due to faster PSRAM, and can record at the maximum OV2640 frame rates with audio for all frame sizes except UXGA, which is capped at 10fps.

Read that as a specification, not a benchmark promise. The README is explicit that actual rate depends on the quality and size of the SD card and the complexity and quality of the images, and it gives a concrete warning: a no-name 4GB SDHC labelled Class 6 was three times slower than a genuine Sandisk 4GB SDHC Class 2. Card choice is not a detail here.

Where ESP32-CAM_MJPEG2SD fails or is the wrong tool

The README states the central limitation plainly: the ESP32 cannot support all of the features as it will run out of heap space. Every optional INCLUDE_* you enable competes with the frame buffer. That is why the README recommends ESP32S3 camera boards for better functionality and performance, naming the Freenove ESP32S3 Cam, ESP32S3 XIAO Sense and an AI Thinker style ESP32-S3-Cam, while advising you to avoid no-name boards marked ESPS3 RE:1.0.

The second failure mode is hardware variance. The README warns that some clone boards have different specs to the original, giving PSRAM size as the example. A board that reports less PSRAM than the code expects will fail in ways that look like software bugs, which is presumably why the maintainer asks for bug reports only when there is an ERR message, an unhandled library error or a crash. If your symptom is a warning about your own configuration, the README treats that as your problem to fix, not a defect.

Third, the streaming and recording paths are not a general purpose NVR. RTSP, MJPEG and remote NVR output are listed as capabilities, but there is no documented retention policy, no documented rollback procedure for a firmware upgrade, and no documented API stability guarantee. The repository is a sketch with a version number, not a library with a compatibility contract. The README does not document rollback, so treat an upgrade as a one-way move unless you keep the previous source yourself.

Alternatives: ESPHome and platform camera stacks

The obvious alternative for a Home Assistant user is ESPHome, which compiles YAML into firmware for the same ESP32 camera boards. The difference in approach is structural rather than cosmetic. ESPHome generates a firmware image from a declarative configuration and exposes entities over the native API; ESP32-CAM_MJPEG2SD is a C++ application you edit and compile, with its own web configuration page, its own MQTT integration and its own file format on the SD card.

That matters at the point of failure. With ESPHome you debug a configuration file and the generated firmware's logs. With this project you debug a header file, a partition scheme and an SD card, and the README gives you a table of expected frame rates to compare against. What you get in return is behaviour ESPHome does not target: writing AVI files with embedded WAV audio to a per-day folder structure, telemetry recording during capture, camera hub access to other ESP32-CAM_MJPEG2SD devices, an intercom feature, and photogrammetry capture. If your requirement is a camera entity in a dashboard, ESPHome is the shorter path. If your requirement is footage on a card that survives the network being down, this project is the one built for that.

Licence, maintenance and the cost of upgrading

The repository is licensed AGPL-3.0. That is a strong copyleft licence with a network clause, and it is worth understanding before you ship a product built on this code or expose a modified version as a service. The practical implication is that distributing modified firmware, or offering modified functionality over a network, carries source disclosure obligations. Nothing here is legal advice; read the licence text and talk to someone qualified if the camera is part of something you sell.

On maintenance, the last push was on 2026-08-29, and the most recent release listed is v10.9.5 on the same date, following v10.9.4 on 2026-05-16 and v10.9.3 on 2026-04-02. The cadence over those three releases is roughly every six to eight weeks, with the newest release adding Ethernet network selection, pins for CAMERA_MODEL_Waveshare_ESP32_S3_ETH, definitions for an external W5500 controller, accelerometer motion detection, OV5640 autofocus and several named issue fixes. The repository is not archived.

Upgrade cost is concentrated in one file and one IDE setting. The README pins a minimum arduino-esp32 core version, v3.1.1, and requires a specific partition scheme per chip family, so a core upgrade is a real event rather than a background update. If you have edited ESP32-CAM_MJPEG2SD.h to select a board and enable features, reapply those edits after pulling a new version, because the header is where your configuration lives.

Editorial conclusion

Adopt it if you have an ESP32S3 camera board, a genuine SD card and the patience to configure one header file, and you want recordings that land on local storage rather than in a cloud account. Do not adopt it if you need a low-power battery node, a stable long-term API, or a build you can debug without reading a large single-repo sketch. Before wiring anything, check that your board's PSRAM size matches the module the README assumes, and confirm which CAMERA_MODEL_* define applies to it.

Frequently asked questions

What is an ESP32-CAM used for in ESP32-CAM_MJPEG2SD?

The README lists security cameras, wildlife monitoring, rocket flight monitoring and FPV vehicle control as example uses. The application records motion-triggered or continuous video to an SD card as AVI files and can stream MJPEG to a browser.

Is the ESP32-CAM the same as an ESP32 in this project?

No. The README distinguishes the ESP32 from the ESP32S3 and states that the ESP32 cannot support all of the features because it will run out of heap space, recommending ESP32S3 camera boards for better functionality and performance.

Does ESP32-CAM_MJPEG2SD support SD cards?

Yes. Recordings are written to an SD card, and the README notes the card is used in MMC 1 line mode by default. Card quality matters: a no-name 4GB SDHC labelled Class 6 was three times slower than a genuine Sandisk 4GB SDHC Class 2.

What can I do with an ESP32-CAM running ESP32-CAM_MJPEG2SD?

Beyond recording, the README lists RTSP streaming, Telegram or email alerts, FTP, HTTPS and WebDAV transfer, MQTT control with Home Assistant integration, pan and tilt servos, telemetry recording, an intercom feature and a camera hub for other devices running the same application.

Official sources

  1. Issues
  2. License: AGPL-3.0
  3. README
  4. Releases
  5. s60sc/ESP32-CAM_MJPEG2SD on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/s60sc-esp32-cam-mjpeg2sd.svg)](https://hysenlabs.com/projects/s60sc-esp32-cam-mjpeg2sd)