ESP32-CAM_MJPEG2SD: JPEG-to-AVI Recording on an ESP32 Camera Board
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.
At a glance
- What is it?
- An Arduino sketch that turns an ESP32 or ESP32S3 camera board into a motion-triggered or continuous recorder, writing AVI files to SD card and streaming MJPEG to a browser. The README is blunt about the hardware ceiling: the classic ESP32 runs out of heap before it runs out of features.
- Who is it for?
- Adopt it if you have a Freenove ESP32S3 Cam, XIAO Sense or AI Thinker style ESP32-S3 board, a genuine branded SD card, and a use case that tolerates 5 fps at XGA and above. Do not adopt it if you need a supported product with a warranty or you are working with a no-name board whose PSRAM size differs from the original.
- 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 18 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 September 15, 2026, and from our analysis. They are not legal advice.
DEEP OPEN-SOURCE ANALYSIS
The problem is file count, not frame capture
An ESP32 camera can take a JPEG. What it cannot do comfortably is write thousands of them to an SD card as separate files and still keep a usable frame rate. Each write carries filesystem overhead, and directory entries pile up fast. ESP32-CAM_MJPEG2SD concatenates the JPEG frames into a single AVI file per recording session, which the README describes as faster than saving individual files and easier to manage, particularly at small image sizes. The AVI container also carries a frame rate, so media players replay the recording at the speed it was captured rather than as a slideshow. If a microphone is fitted, a WAV file is written and stored inside the AVI. The audience is people building security cameras, wildlife traps, rocket flight monitors and FPV vehicles on ESP32 hardware, plus anyone who wants an MJPEG view in a browser without running a separate server process.
PSRAM buffering and the SD write path
The design leans on the camera module's PSRAM, 4MB on the classic ESP32 and 8MB on most ESP32S3 boards. Frames are buffered there while the AVI file is assembled, which cuts the number of SD writes and aligns them with the card's sector size. Playback runs the opposite way: the AVI is read from SD into a multi-sector buffer and pushed to the browser as timed individual frames. By default the card runs in MMC 1 line mode. The README's reasoning is that on the ESP32 this is practically as fast as 4 line mode and it frees pin 4, which is wired to the onboard lamp, and pin 12, which can then take a PIR sensor. On the ESP32S3 the same choice costs real throughput: the README cites tests by a contributor showing 4 line mode roughly doubles speed. That is a configuration decision most users will never revisit, and on S3 hardware it is the wrong default.
Frame rates fall off a cliff above VGA
The README publishes a table of measured 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. Below VGA the application keeps up with the sensor closely: 45 fps against a 50 fps sensor ceiling at 96x96 through 240x240, 40 against 50 at QVGA through HVGA. At VGA both drop to 25 and 20. At XGA and everything above, the sensor manages 12.5 fps and the application records 5. That is the practical boundary of this project on ESP32 hardware. The README notes that raising the clock to 24MHz on ESP32S3 lifts the maximums from 50 to 60 and 25 to 30 fps, possibly at the cost of JPEG quality, and that the S3 runs the app at roughly double the ESP32's speed thanks to faster PSRAM, reaching the sensor's maximum frame rates at all sizes except UXGA, which caps at 10 fps. Detection time is also tabulated, from 15ms at 96x96 to 450ms at UXGA, so motion responsiveness degrades alongside frame rate.
Building it in the Arduino IDE
The install path is manual: download the GitHub files into the Arduino IDE sketch folder and remove the -master suffix from the application folder name. Compile against arduino-esp32 core v3.1.1 or later, which the README says contains network fixes and frame selection changes. Board selection happens by uncommenting exactly one #define CAMERA_MODEL_* line in ESP32-CAM_MJPEG2SD.h, unless you are using one of the two defaults: CAMERA_MODEL_AI_THINKER for the ESP32 Cam board and CAMERA_MODEL_FREENOVE_ESP32S3_CAM for the Freenove ESP32S3 Cam. Optional features are off by default and are enabled by setting the relevant #define INCLUDE_* to true in the same header. In the IDE you select the ESP32 or ESP32S3 Dev Module, enable PSRAM, and pick a partition scheme: Minimal SPIFFS for ESP32, or 8M with spiffs or 16MB(3MB APP...) for ESP32S3. The README states that the ESP32 cannot support all features because it runs out of heap space.
The feature list is wider than the heap
Motion detection, continuous or time lapse recording, I2S and PDM audio, pan and tilt servos, RTSP serving video and audio and subtitles, telemetry capture, vehicle remote control, Telegram and email alerts, concurrent browser and NVR streaming over HTTP or RTSP, FTP and HTTPS and WebDAV transfer, MQTT with Home Assistant integration, an external heartbeat, a camera hub for reaching other ESP32-CAM_MJPEG2SD devices, photogrammetry capture, an intercom mode, and an optional Ethernet path instead of WiFi with pins defined for an external W5500 controller. That is not a feature list so much as a set of mutually competing demands on the same 4MB of PSRAM. The README says so directly. The consequence is that the feature set you can actually enable depends on your board, and the README's own advice is to use an ESP32S3 board for better functionality and performance. It also warns against no-name boards marked ESPS3 RE:1.0, and notes that clone boards can differ from the original in specifications such as PSRAM size. If your board is a clone with less PSRAM than advertised, features will fail in ways that look like bugs but are not.
Wrong tool for a supported deployment
This is a single-maintainer Arduino sketch, not a firmware product. The README's own note is unusually direct for an open source project: some users raise issues when the app reports a warning, but the warning is the app telling the user something is wrong with their setup, and only the user can fix it. Issues are requested for actual bugs such as ERR messages, unhandled library errors or crashes, and enhancement suggestions belong in Discussions. That is a reasonable policy, and it also tells you what you are adopting. If you want a camera you can deploy and forget, with a vendor to call when it misbehaves, this is the wrong shape of project. It is also the wrong tool if your application needs high-resolution video at a usable frame rate. Five fps at XGA and above is a motion-detection feed, not footage you would hand to someone as evidence or edit into anything.
Against Frigate plus a dedicated camera
The obvious alternative for a home or small-site setup is a network camera feeding a separate NVR or detection service such as Frigate, with the heavy lifting done off the camera. The difference in approach is where the work happens. Frigate expects a camera that exposes a stream and does detection on a host with real CPU or a Coral accelerator, so you get high resolution, many cameras and retained history without fighting PSRAM. ESP32-CAM_MJPEG2SD inverts that: detection, encoding, storage and serving all run on a board with megabytes of RAM, which is why the feature list has to be trimmed to fit and why frame rates collapse at high resolution. What the ESP32 route buys is cost, power draw and the absence of a server. A battery-powered wildlife camera in a field does not have a host machine to call home to. If you have mains power and a network drop, the Frigate-style split is the more capable architecture and you should not pretend otherwise.
Maintenance, licensing and what a version bump costs you
The project is active, with v10.9.5 released on 2026-08-29, v10.9.4 in May 2026 and v10.9.3 in April 2026. The changelog for the current version lists Ethernet network selection, new pins for the Waveshare ESP32-S3-ETH board, external W5500 pin definitions, accelerometer motion detection via MPU6050 or MPU9250, OV5640 autofocus support, logging and memory improvements, two issue fixes and a night time duration setting based on location. The upgrade cost is not the release cadence, it is the configuration surface: board defines, INCLUDE_* flags and partition scheme all live in one header, and a release that changes pin definitions or adds a board variant can require you to re-check that header against your hardware. Licence is AGPL-3.0. That is a strong copyleft with a network clause, so if you run a modified version as a network service, the licence's source-availability terms are likely to apply to your modified version. This is not legal advice; check with someone qualified before shipping a modified build as a service.
Editorial conclusion
Adopt it if you have a Freenove ESP32S3 Cam, XIAO Sense or AI Thinker style ESP32-S3 board, a genuine branded SD card, and a use case that tolerates 5 fps at XGA and above. Do not adopt it if you need a supported product with a warranty or you are working with a no-name board whose PSRAM size differs from the original. Verify three things before committing: that your board is not marked ESPS3 RE:1.0, that your SD card is not a slow clone, and that the AGPL-3.0 obligations fit how you intend to distribute the firmware.
Community notes