The public API is in namespace NiusCam. Most sketches add
using namespace NiusCam; once, then use ordinary objects and methods:
Camera camera;
Config config;
config.useBalanced();
Result result = camera.begin(config);| Header | Public functionality |
|---|---|
<NiusCam.h> |
Camera, frame ownership, board/config types, sensor controls, diagnostics, errors |
<NiusCamAutofocus.h> |
Optional OV5640 autofocus controller, mode, and status |
<NiusCamOv2640.h> |
Optional OV2640 automatic low-light frame-rate controls |
<NiusCamStorage.h> |
microSD mounting, information, atomic JPEG saving, sequential files, bounded recording |
<NiusCamHttp.h> |
HTTP index, snapshot, and MJPEG server |
<NiusCamUdp.h> |
Chunked JPEG UDP sender and protocol types |
Only include optional modules that the sketch uses.
Fallible configuration, lifecycle, control, storage, and network methods return
Result.
Result result = camera.begin();
if (!result) {
Serial.print("Camera error: ");
Serial.print(result.message());
Serial.print("; native code: 0x");
Serial.println(result.native, HEX);
}| Member | Meaning |
|---|---|
error |
Portable Error enum |
native |
Original esp_err_t where available |
ok() |
true for success |
operator bool() |
Allows if (result) and if (!result) |
message() |
Short static English description |
Error values are None, InvalidArgument, InvalidState, Unsupported,
NoMemory, CameraNotFound, SensorMismatch, CaptureFailed,
StorageFailed, NetworkFailed, Timeout, and NativeError.
Camera camera;
Result begin();
Result begin(const Config &config);
Result begin(const BoardProfile &board, const Config &config);In actual code these are called as camera.begin(...). Startup validates the
configuration, checks it against the expected sensor capabilities, initializes
the camera driver, detects the physical sensor, optionally enforces identity,
and activates the selected staged or capability-gated direct DMA path.
Calling begin() on an already initialized object returns InvalidState.
Frame frame = camera.capture();
if (frame) {
// Use the frame.
}Capture makes one bounded retry when the underlying driver initially returns no
frame. An empty Frame indicates failure. The core capture method does not
allocate or copy a second image buffer.
| Method | Behavior |
|---|---|
reconfigure(config) |
Restarts with a new driver configuration; if startup fails, attempts to restore the previous working configuration |
recover() |
Deinitializes and restarts the current board/configuration after a camera fault |
end() |
Releases the camera driver and frame buffers |
isReady() |
Reports whether the camera is currently initialized |
Release every live Frame before any lifecycle transition.
Result result = camera.suspend(PowerState::DriverOff);
// Application-controlled sleep or idle period.
result = camera.resume();PowerState |
Behavior |
|---|---|
Active |
Camera initialized and available |
DriverOff |
Camera driver and frame buffers deinitialized |
BoardPowerDown |
Driver deinitialized and PWDN asserted when that GPIO is wired |
BoardPowerDown returns Unsupported on boards whose profile has no PWDN pin.
The application remains responsible for Wi-Fi sleep, ESP32 light/deep sleep,
CPU frequency, and peripheral shutdown.
| Method | Returns |
|---|---|
board() |
Active BoardProfile, or null before a board is selected |
config() |
Last selected Config |
sensor() |
A SensorControls object for the active sensor |
nativeSensor() |
Espressif sensor_t* escape hatch |
capabilities() |
Capability record for the detected sensor |
diagnostics() |
Current lifecycle, sensor, PSRAM, DMA, and memory information |
Frame is move-only RAII ownership for camera_fb_t.
| Method | Description |
|---|---|
operator bool() |
Whether a frame buffer is held |
data() |
Read-only pointer to frame bytes |
size() |
Byte count |
width() / height() |
Captured dimensions |
nativeFormat() |
Espressif pixformat_t |
timestampUs() |
Driver timestamp in microseconds |
native() |
Native camera_fb_t* access |
release() |
Return the buffer before scope exit |
Copy construction and copy assignment are disabled. Move construction and move assignment transfer ownership safely.
Pointers from data() and native() become invalid after release. Do not call
esp_camera_fb_return() yourself for a frame still owned by Frame.
Config contains profile, pixelFormat, frameSize, jpegQuality,
frameBuffers, bufferLocation, grabMode, xclkHz, dmaMode, the legacy
psramDma compatibility flag, and validateSensor.
Config config; // Balanced
config.useEco(); // Replace every field with Eco defaults
config.useBalanced(); // Replace every field with Balanced defaults
config.useTurbo(); // Replace every field with Turbo defaults
config.useStagedDma(); // Select the portable staged path
config.useDirectDma(); // Select and tune the optional direct pathStatic factories Config::Eco(), Config::Balanced(), and Config::Turbo()
are also available, but object methods are preferred in examples for clarity.
See Configuration and profiles for exact values, formats,
frame sizes, memory behavior, and tuning.
useDirectDma() is currently hardware-verified for ESP32-S3 with OV5640 QSXGA
JPEG. It selects two or more Latest-mode PSRAM buffers, a safe JPEG-quality
floor, and 20 MHz XCLK. camera.begin() rejects direct requests outside the
verified constraints. useStagedDma() restores the default camera-GDMA to
internal-SRAM path followed by the driver copy into the PSRAM frame buffer.
Built-in profiles describe ESP32-S3-CAM N16R8, AI-Thinker ESP32-CAM, and XIAO
ESP32-S3 Sense. Automatic selection is used by camera.begin() and
storage.begin().
An explicit board is available for generic board targets:
const BoardProfile &board = BoardProfiles::AiThinkerEsp32Cam();
Result result = camera.begin(board);BoardProfile contains a name, CameraPins, StoragePins, expected
SensorModel, verification metadata, and an optional CameraModuleInfo
pointer. Module metadata describes the lens angle and flex-cable length plus
declaredOpticalFilter, declaredIrPassWavelengthNm, and
declaredNightVisionCapable. These fields record vendor claims; they do not
constitute spectral verification or imply an integrated IR illuminator. See
Supported hardware before creating a custom profile.
Generic built-in profiles auto-detect OV2640, OV3660, or OV5640 on the selected board wiring. Exact module methods are available when sensor mismatch rejection and optical metadata are required:
Esp32S3CamOv2640Standard()Esp32S3CamOv5640Fixed130()Esp32S3CamOv5640Autofocus50mm()AiThinkerEsp32CamOv2640Wide850nm()AiThinkerEsp32CamOv5640Fixed65()XiaoEsp32S3SenseOv3660Standard()XiaoEsp32S3SenseOv2640Wide650nm()
An exact profile validates the detected sensor ID; it cannot identify how a vendor routed that sensor on the flex. Before using the OV5640 profile, compare the module datasheet with the supported 24-pin DVP layout. MIPI CSI-2 variants are not supported by the ESP32 camera peripheral.
Autofocus is an optional object bound to an initialized OV5640 camera:
Autofocus autofocus;
Result result = autofocus.begin(camera, AutofocusMode::SingleShot);
if (result)
result = autofocus.trigger();
AutofocusStatus focusInfo;
if (result)
result = autofocus.wait(focusInfo, 5000);It supports SingleShot and Continuous modes. status() reports initialized,
focused, busy, and the sensor-native status byte. end() stops the controller;
available() reports readiness. See OV5640 autofocus.
Ov2640Controls applies the sensor manufacturer's 24 MHz automatic frame-rate
sequences without changing the image to grayscale:
Ov2640Controls ov2640;
Result result = ov2640.begin(camera);
if (result)
result = ov2640.automaticFrameRate60Hz();automaticFrameRate50Hz() selects the documented 50 Hz sequence and
restoreTiming() restores the values captured by begin(). See
OV2640 low-light timing.
camera.capabilities() returns:
- Sensor model and native dimensions
- Minimum and maximum frame-size enums
- Allowed frame-size masks for JPEG, grayscale, RGB-family, and RAW outputs
- Whether the verified sensor path permits PSRAM DMA
- Whether sharpness and denoise controls are available
Use the portable helper before changing driver configuration:
const SensorCapabilities &caps = camera.capabilities();
if (supports(caps, PixelFormat::Jpeg, FrameSize::UXGA)) {
// This sensor/format/size combination is represented as supported.
}sensorName(model), identifySensor(id), sensorCapabilities(model),
nativePixelFormat(format), and nativeFrameSize(size) support diagnostic and
advanced integration code.
Obtain a lightweight control object after successful camera startup:
SensorControls sensor = camera.sensor();
if (!sensor.available()) {
Serial.println("Sensor is unavailable");
}| Method | Portable range / values |
|---|---|
brightness(value) |
-2 to 2 |
contrast(value) |
-2 to 2 |
saturation(value) |
-2 to 2 |
sharpness(value) |
-3 to 3; sensor-dependent |
denoise(value) |
0 to 8; sensor-dependent |
specialEffect(value) |
None, Negative, Grayscale, RedTint, GreenTint, BlueTint, Sepia |
| Method | Meaning |
|---|---|
automaticExposure(enabled) |
Sensor exposure-control loop |
dspAutomaticExposure(enabled) |
Secondary DSP AEC algorithm where supported |
exposureValue(value) |
Manual/native exposure value, 0-1200 |
exposureLevel(value) |
Automatic-exposure bias, -2 to 2 |
automaticGain(enabled) |
Automatic gain-control loop |
gain(value) |
Manual/native gain value, 0-30 |
gainCeiling(value) |
Automatic gain ceiling from X2 through X128 |
Disable the corresponding automatic loop before expecting a manual exposure or gain value to remain fixed.
| Method | Meaning |
|---|---|
automaticWhiteBalance(enabled) |
Main automatic white-balance loop |
whiteBalanceGain(enabled) |
Automatic white-balance gain application |
whiteBalanceMode(value) |
Auto, Sunny, Cloudy, Office, or Home |
horizontalMirror(enabled) |
Mirror left/right |
verticalFlip(enabled) |
Flip top/bottom |
| Method | Meaning |
|---|---|
testPattern(enabled) |
Sensor color-bar/test pattern |
lensCorrection(enabled) |
Lens-shading correction |
rawGamma(enabled) |
Raw gamma stage |
badPixelCorrection(enabled) |
Bad-pixel correction |
whitePixelCorrection(enabled) |
White-pixel correction |
downsize(enabled) |
Sensor downsize/crop stage |
reset() |
Reset sensor registers through the bundled driver |
frameSize(value), pixelFormat(value), and jpegQuality(value) call the
sensor's live operations. For changes that materially alter memory requirements
or are followed by sustained capture, prefer updating a Config and calling
camera.reconfigure() so driver buffers match the intended output.
sensor.native() returns Espressif's sensor_t*. It permits register, PLL,
window, and sensor-specific operations not represented by NiusCam. Native calls
bypass portable validation and can make the current Config description stale.
Diagnostics reports:
| Field | Meaning |
|---|---|
initialized |
Camera driver is active |
psramAvailable |
Arduino core detected PSRAM |
psramDmaRequested |
Current config requested DMA |
psramDmaEnabled |
Capability-gated DMA path was actually enabled |
dmaModeRequested |
Typed requested mode: Staged or Direct |
dmaModeActive |
Typed active mode after initialization |
sensor / sensorPid |
Detected sensor identity |
freeHeap / freePsram |
Current free memory snapshots |
powerState |
Current NiusCam camera lifecycle state |
Memory values are observations at call time, not allocation guarantees.
Include <NiusCamStorage.h> and construct Storage storage;.
| Method | Behavior |
|---|---|
begin(frequencyHz) |
Mount storage from the automatic board profile; default 10 MHz request with bounded fallback |
begin(board, frequencyHz) |
Mount storage from an explicit/custom board profile |
end() |
Unmount and release the selected bus |
mounted() |
Whether a filesystem object is active |
info() |
Capacity, used/free bytes, mount state, and bus |
saveJpeg(frame, path, atomic, callback, context) |
Save one JPEG, optionally using a temporary file and progress callback |
saveNextJpeg(frame, directory, prefix, sequence) |
Create a collision-free six-digit filename |
recordJpegs(camera, count, intervalMs, directory, saved) |
Capture a bounded sequence at a requested interval |
native() |
Underlying Arduino fs::FS* |
saveJpeg() rejects non-JPEG frames. See Storage guide for
examples, progress fields, card preparation, and error handling.
Include <NiusCamHttp.h> and create HttpStreamServer server;.
| Method | Behavior |
|---|---|
begin(camera, port) |
Start the server; default port 80 |
end() |
Stop the server and release its handle |
running() |
Whether the server is active |
Routes are /, /snapshot.jpg, and /stream. Camera output must be JPEG. The
application configures Wi-Fi and must not capture concurrently while a stream
client owns capture. See Streaming guide.
Include <NiusCamUdp.h> and create UdpFrameSender sender;.
| Method | Behavior |
|---|---|
begin(destination, destinationPort, localPort, packetIntervalUs) |
Open a unicast sender; destination defaults to port 5005 |
send(frame) |
CRC and packetize one JPEG frame into payloads of at most 1200 bytes |
end() |
Close the UDP socket |
frameId() |
Last assigned monotonic frame identifier |
crc32(data, length) |
Protocol-compatible complete-frame CRC32 |
send() is synchronous and returns after the frame's packets have been queued
or a bounded retry fails. It rejects non-JPEG frames. See
UDP protocol.
NiusCam deliberately adds no mutex and no background capture task. Serialize camera lifecycle and capture in one task, or protect them with an application-level lock.
- Do not capture in
loop()while an HTTP MJPEG client is streaming. - Do not reconfigure, recover, suspend, or end while a
Frameexists. - Do not access the same
StorageorUdpFrameSenderobject concurrently without application synchronization. - Keep callbacks short; a storage progress callback runs inside the write.
These rules avoid hidden copies, tasks, and persistent synchronization overhead.