A production-minded Arduino camera library for ESP32 camera modules.
NiusCam provides a small, typed C++ interface for camera capture, sensor
control, microSD storage, browser streaming, and low-latency UDP delivery. It
uses the esp32-camera driver already bundled with Arduino-ESP32, then adds
board-aware configuration, safe frame ownership, capability checks, recovery,
an optional NiusCam-managed direct-PSRAM DMA mode for the verified OV5640/S3
path, and optional modules that are removed by the linker when unused.
The library is designed for common e-commerce camera boards and for projects that need to move beyond a one-file camera demo without inheriting a large web application or a second camera-driver package.
- Simple
Camera camera;andcamera.method()workflow - Automatic board profile selection for the supported targets
- Move-only
Frameobjects that return frame buffers automatically - Eco, Balanced, and Turbo starting profiles plus complete custom configuration
- Typed controls for exposure, gain, white balance, image tuning, orientation, test patterns, frame format, resolution, clocks, buffers, and PSRAM DMA
- Sensor detection, capability checks, profile mismatch protection, diagnostics, controlled reconfiguration, and recovery
- Optional OV5640 single-shot and continuous autofocus control
- Optional OV2640 datasheet-based automatic low-light frame-rate control
- Optional validated OV5640 direct-to-PSRAM DMA at full resolution
- Optional atomic JPEG storage with progress, throughput, and ETA reporting
- Optional HTTP snapshots and MJPEG browser streaming
- Optional versioned UDP framing with sequence metadata and CRC32
- No bundled credentials, cloud service, telemetry, or copied camera HAL
| Camera module / board | Camera sensor | Maximum native sensor size | microSD interface | Status |
|---|---|---|---|---|
| ESP32-S3-CAM N16R8 | OV2640, standard 66-degree lens | 1600 x 1200 | SD_MMC, 1-bit | Camera and storage verified |
| ESP32-S3-CAM N16R8 | OV5640 fixed-focus, 130-degree lens | 2592 x 1944 | SD_MMC, 1-bit | Focused 2560 x 1920 scene and optional direct-PSRAM DMA verified |
| AI-Thinker ESP32-CAM | OV2640, 160-degree lens, seller-declared visible + 850 nm dual-pass filter | 1600 x 1200 | SD_MMC, 1-bit | Capture and wide-angle output verified; optical passband unverified |
| AI-Thinker ESP32-CAM | OV5640 fixed-focus, 65-degree lens | 2592 x 1944 | SD_MMC, 1-bit | Sensor and sharp decoded 2560 x 1920 JPEG verified |
| Seeed Studio XIAO ESP32-S3 Sense | OV3660, standard 66-degree lens | 2048 x 1536 | SPI | Camera and storage verified |
| Seeed Studio XIAO ESP32-S3 Sense | OV2640, 160-degree lens, seller-declared 650 nm IR-cut filter | 1600 x 1200 | SPI | Capture and wide-angle output verified; optical cutoff unverified |
| Sensor | Support level | Notes |
|---|---|---|
| OV2640 | Hardware verified | JPEG, grayscale, RGB565, YUV422, and RGB888 capability paths; sharpness and denoise are not exposed by the bundled sensor driver |
| OV3660 | Hardware verified | JPEG and grayscale through 2048 x 1536; capability-gated RGB modes; sharpness and denoise available |
| OV5640 | Hardware verified on two DVP boards | Fixed-focus capture verified at 2560 x 1920; sensor native array is 2592 x 1944; optional AF controller requires compatible actuator hardware |
Different view angles, focal lengths, fixed-focus lenses, IR-cut filters, and
night-vision assemblies normally do not require a different software driver if
the sensor model and electrical pinout are unchanged. A module with different
wiring needs a custom BoardProfile.
Warning
The tested 50 mm DC-5640 autofocus flex is a two-lane MIPI CSI-2
module, despite being sold alongside ESP32 camera products. Its pins 12-22
carry MIPI differential lanes rather than the DVP pixel bus required by the
ESP32-S3. SCCB can still identify the sensor, but this module cannot deliver
frames through an ESP32-S3-CAM DVP connector. Use an explicitly documented
24-pin DVP OV5640 module; connector size and sensor name alone are not enough.
Compare the vendor's complete table with the
supported 24-pin DVP layout
before powering the module; voltage rails and AF power must match as well as
signal names.
Lens angle and optical-filter type are descriptive module metadata. The seller's 650 nm option is an IR-cut daylight filter; its 850 nm night option is a visible-light + 850 nm dual-pass filter, not an IR emitter. A decoded RGB image can verify field of view and low-light sensitivity, but cannot prove the exact filter wavelength. OV2640 automatic low-light timing preserves color and extends exposure by reducing frame rate; it does not create IR illumination.
Note
NiusCam supports Arduino-ESP32 3.x. All examples were compiled with 3.0.7 and 3.3.10; physical hardware results use 3.3.10. Arduino-ESP32 2.x is not a supported target.
In Arduino IDE, open File > Preferences (Windows/Linux) or Arduino IDE > Settings (macOS). Add this URL to Additional boards manager URLs:
https://espressif.github.io/arduino-esp32/package_esp32_index.json
If the field already contains another URL, click its list button and add the Espressif URL on a new line. Do not remove package URLs used by other boards.
- Open Tools > Board > Boards Manager.
- Search for
esp32. - Install esp32 by Espressif Systems, version 3.x.
- Restart Arduino IDE if the new boards do not immediately appear.
No separate esp32-camera library is needed. Installing another copy can cause
header or linker conflicts because the camera component is included in the
Espressif board package. Espressif also maintains the
official Arduino-ESP32 installation guide.
For a release ZIP:
- Open Sketch > Include Library > Add .ZIP Library.
- Select the NiusCam ZIP without extracting it first.
- Confirm that File > Examples > NiusCam contains the bundled examples.
See Arduino's library installation guide for IDE-specific screenshots and alternative installation methods.
For a source checkout, place the repository in the Arduino sketchbook as:
<Arduino sketchbook>/libraries/NiusCam/
The directory containing library.properties, src, and examples must be
named NiusCam; avoid an extra nested directory such as
NiusCam-main/NiusCam.
| Hardware | Arduino IDE board | Required/recommended options |
|---|---|---|
| ESP32-S3-CAM N16R8 | ESP32S3 Dev Module | Flash Size: 16MB, PSRAM: OPI PSRAM; choose a partition scheme large enough for the application |
| AI-Thinker ESP32-CAM | AI Thinker ESP32-CAM | Use the board defaults; connect the required USB-to-serial adapter and stable 5 V supply |
| XIAO ESP32-S3 Sense | XIAO_ESP32S3 | PSRAM: OPI PSRAM; ensure the Sense expansion board and camera ribbon are fully seated |
Then choose the upload port under Tools > Port. Enabling PSRAM is strongly recommended for JPEG camera use and required for larger frames or multiple frame buffers.
- Open File > Examples > NiusCam > BasicCapture.
- Upload the sketch.
- Open Tools > Serial Monitor at 115200 baud.
- Confirm that the detected sensor, free heap, free PSRAM, resolution, and JPEG byte count are printed.
If initialization fails, follow the checks in Troubleshooting before changing pin definitions.
The default Config is Balanced, and the correct built-in board profile is
selected from the Arduino board target:
#include <NiusCam.h>
using namespace NiusCam;
Camera camera;
void setup() {
Serial.begin(115200);
Result result = camera.begin();
if (!result) {
Serial.print("Camera initialization failed: ");
Serial.println(result.message());
}
}
void loop() {
Frame frame = camera.capture();
if (!frame) {
Serial.println("Capture failed");
delay(1000);
return;
}
Serial.print(frame.width());
Serial.print('x');
Serial.print(frame.height());
Serial.print(" JPEG, ");
Serial.print(frame.size());
Serial.println(" bytes");
delay(1000);
}Frame owns one camera frame buffer. It is move-only and automatically returns
that buffer when the object leaves scope. Call frame.release() before a long
wait, reconfiguration, suspension, or any operation that needs the same PSRAM.
Profiles are safe, documented starting points—not hidden modes. Every field can be changed after applying a profile.
| Setting | Eco | Balanced (default) | Turbo |
|---|---|---|---|
| Pixel format | JPEG | JPEG | JPEG |
| Frame size | QVGA, 320 x 240 | VGA, 640 x 480 | VGA, 640 x 480 |
| JPEG quality value | 16 | 12 | 14 |
| Frame buffers | 1 | 2 | 3 |
| Buffer memory | PSRAM | PSRAM | PSRAM |
| Grab mode | When empty | Latest frame | Latest frame |
| Camera XCLK | 10 MHz | 20 MHz | 20 MHz |
| DMA mode | Staged | Staged | Staged |
| Primary goal | Lower clock/buffer use | General capture and stability | Delivered frame rate and low latency |
JPEG quality follows the Espressif sensor convention: lower values request higher image quality and usually create larger files. Valid values are 0-63, but useful values for these sensors are commonly around 10-20.
Object-style configuration keeps sketches readable:
Config config;
config.useTurbo();
config.frameSize = FrameSize::SVGA;
config.jpegQuality = 10;
config.frameBuffers = 2;
Result result = camera.begin(config);The requested format and frame size are checked against the selected sensor before camera startup. All profiles use the broadly compatible staged path; direct PSRAM DMA is an explicit opt-in with stricter validation. See Configuration and profiles for every field, enum, and sensor constraint.
On the verified ESP32-S3/OV5640 path, direct mode writes QSXGA JPEG data from the camera peripheral into PSRAM without the staged CPU copy:
Config config;
config.useBalanced();
config.frameSize = FrameSize::QSXGA;
config.useDirectDma();
Result result = camera.begin(config);useDirectDma() selects the verified clock, JPEG quality floor, two-buffer
Latest configuration, and direct backend. Other OV5640 resolutions continue to
use staged DMA. Direct mode is optional and is intended to reduce camera-copy
CPU work; it is not guaranteed to increase sensor-limited FPS. See
Configuration and profiles.
SensorControls sensor = camera.sensor();
sensor.brightness(1);
sensor.contrast(0);
sensor.automaticExposure(true);
sensor.automaticGain(true);
sensor.automaticWhiteBalance(true);
sensor.horizontalMirror(false);
sensor.verticalFlip(false);All controls return Result; production code should check the result when a
control is required. Sensor-specific features remain available through
sensor.native().
#include <NiusCamAutofocus.h>
Autofocus autofocus;
Result result = autofocus.begin(camera, AutofocusMode::SingleShot);
if (result)
result = autofocus.trigger();
AutofocusStatus focusInfo;
if (result)
result = autofocus.wait(focusInfo);Autofocus is optional and linked only when its header is included. See the autofocus guide for modes, status values, and electrical requirements.
#include <NiusCamStorage.h>
Storage storage;
Result result = storage.begin();
Frame frame = camera.capture();
if (result && frame)
result = storage.saveJpeg(frame, "/photo.jpg", true);The atomic mode writes /photo.jpg.part, flushes it, and renames it only after
the complete JPEG is stored. The SaveToSD example adds a progress bar,
throughput, ETA, and explicit error messages. See
Storage guide.
#include <NiusCamHttp.h>
HttpStreamServer server;
Result result = server.begin(camera);The application owns Wi-Fi setup. The server provides /, /snapshot.jpg, and
/stream. The bundled example creates a local SoftAP with no personal network
credentials. See Streaming guide.
#include <NiusCamUdp.h>
UdpFrameSender sender;
sender.begin(IPAddress(192, 168, 4, 2));
Frame frame = camera.capture();
if (frame)
sender.send(frame);UDP frames use a documented versioned header, 1200-byte payload chunks, sequence information, total size, and complete-frame CRC32. Receivers must drop incomplete frames. See the streaming guide and UDP protocol.
| Example | Purpose |
|---|---|
BasicCapture |
Initialize, identify the sensor, inspect memory, and capture JPEG frames |
FullControl |
Customize a profile and apply portable sensor controls |
Autofocus |
Run an OV5640 single-shot autofocus cycle and capture focused frames |
Ov2640LowLight |
Apply OmniVision's documented automatic low-light frame-rate timing |
Ov5640DirectDma |
Opt into validated full-resolution direct-to-PSRAM DMA on ESP32-S3 |
SaveToSD |
Mount the correct storage bus and save an atomic JPEG with progress reporting |
PowerModes |
Release a frame, suspend the camera, and restore capture safely |
HttpStream |
Create a SoftAP and serve snapshots plus browser MJPEG |
UdpStream |
Create a SoftAP and send CRC-protected, chunked JPEG frames over UDP |
- Documentation home — choose a guide by task or experience level
- Getting started — installation, board setup, first upload, and validation
- Supported hardware — boards, sensors, storage buses, custom modules, and compatibility boundaries
- Configuration and profiles — every configuration field, profile defaults, formats, frame sizes, memory, and tuning guidance
- OV5640 autofocus — initialization, single-shot and continuous modes, status, and module compatibility
- OV2640 low-light timing — documented 50/60 Hz automatic frame-rate controls and measured dark-scene behavior
- API reference — lifecycle, frames, controls, diagnostics, storage, HTTP, UDP, errors, and native access
- Storage guide — card preparation, progress reporting, atomic files, sequences, recording, and failure handling
- Streaming guide — SoftAP/station setup, HTTP routes, MJPEG, UDP, ownership, and transport selection
- UDP protocol v1 — binary packet format and receiver requirements
- Troubleshooting — camera, PSRAM, microSD, build, upload, and network diagnostics
- Verified results — reproducible hardware observations, limits, and benchmark scope
NiusCam does not copy or fork a third-party camera driver. It uses Espressif's
Apache-2.0 esp32-camera component distributed with Arduino-ESP32 because that
component contains the SoC camera HAL and sensor implementations. NiusCam adds a
consistent public API and optional application-facing services around it.
Only include the modules a sketch uses:
<NiusCam.h>— camera core and controls<NiusCamAutofocus.h>— optional OV5640 autofocus controller<NiusCamOv2640.h>— optional OV2640 low-light timing controls<NiusCamStorage.h>— microSD operations<NiusCamHttp.h>— HTTP snapshots and MJPEG<NiusCamUdp.h>— UDP frame sender
Unused optional modules are not linked into the final firmware.
NiusCam is licensed under the Apache License 2.0. It uses, but does not vendor,
Espressif's Apache-2.0 esp32-camera component supplied by Arduino-ESP32. See
LICENSE and NOTICE.