224 lines
6.9 KiB
C++
224 lines
6.9 KiB
C++
/**
|
|
* Claude's Eyes - Camera Module Implementation
|
|
*
|
|
* OV5640 with 120° wide-angle lens on Waveshare ESP32-S3-Touch-LCD-2
|
|
*/
|
|
|
|
#include "camera.h"
|
|
#include "config.h"
|
|
|
|
// Global instance
|
|
CameraModule Camera;
|
|
|
|
CameraModule::CameraModule()
|
|
: _initialized(false)
|
|
, _lastError(nullptr)
|
|
, _currentResolution(RES_VGA)
|
|
, _currentQuality(DEFAULT_JPEG_QUALITY)
|
|
{
|
|
}
|
|
|
|
bool CameraModule::begin() {
|
|
#if !ENABLE_CAMERA
|
|
_lastError = "Camera disabled in config";
|
|
return false;
|
|
#endif
|
|
|
|
// Camera configuration
|
|
camera_config_t config;
|
|
config.ledc_channel = LEDC_CHANNEL_0;
|
|
config.ledc_timer = LEDC_TIMER_0;
|
|
config.pin_d0 = CAMERA_PIN_D0;
|
|
config.pin_d1 = CAMERA_PIN_D1;
|
|
config.pin_d2 = CAMERA_PIN_D2;
|
|
config.pin_d3 = CAMERA_PIN_D3;
|
|
config.pin_d4 = CAMERA_PIN_D4;
|
|
config.pin_d5 = CAMERA_PIN_D5;
|
|
config.pin_d6 = CAMERA_PIN_D6;
|
|
config.pin_d7 = CAMERA_PIN_D7;
|
|
config.pin_xclk = CAMERA_PIN_XCLK;
|
|
config.pin_pclk = CAMERA_PIN_PCLK;
|
|
config.pin_vsync = CAMERA_PIN_VSYNC;
|
|
config.pin_href = CAMERA_PIN_HREF;
|
|
config.pin_sccb_sda = CAMERA_PIN_SIOD;
|
|
config.pin_sccb_scl = CAMERA_PIN_SIOC;
|
|
config.pin_pwdn = CAMERA_PIN_PWDN;
|
|
config.pin_reset = CAMERA_PIN_RESET;
|
|
config.xclk_freq_hz = CAMERA_XCLK_FREQ;
|
|
config.pixel_format = PIXFORMAT_JPEG;
|
|
config.frame_size = DEFAULT_FRAME_SIZE;
|
|
config.jpeg_quality = _currentQuality;
|
|
config.fb_count = 2; // Double buffering for better performance
|
|
config.fb_location = CAMERA_FB_IN_PSRAM;
|
|
config.grab_mode = CAMERA_GRAB_LATEST;
|
|
|
|
// Check for PSRAM
|
|
if (psramFound()) {
|
|
config.fb_count = 2;
|
|
config.grab_mode = CAMERA_GRAB_LATEST;
|
|
Serial.println("[Camera] PSRAM found, using double buffering");
|
|
} else {
|
|
config.fb_count = 1;
|
|
config.fb_location = CAMERA_FB_IN_DRAM;
|
|
Serial.println("[Camera] No PSRAM, using single buffer");
|
|
}
|
|
|
|
// Initialize camera
|
|
esp_err_t err = esp_camera_init(&config);
|
|
if (err != ESP_OK) {
|
|
_lastError = "Camera init failed";
|
|
Serial.printf("[Camera] Init failed with error 0x%x\n", err);
|
|
return false;
|
|
}
|
|
|
|
// Get sensor and apply settings
|
|
sensor_t* sensor = esp_camera_sensor_get();
|
|
if (sensor) {
|
|
// OV5640 specific settings
|
|
sensor->set_brightness(sensor, 0); // -2 to 2
|
|
sensor->set_contrast(sensor, 0); // -2 to 2
|
|
sensor->set_saturation(sensor, 0); // -2 to 2
|
|
sensor->set_special_effect(sensor, 0); // 0 = No Effect
|
|
sensor->set_whitebal(sensor, 1); // Auto white balance
|
|
sensor->set_awb_gain(sensor, 1); // Auto WB gain
|
|
sensor->set_wb_mode(sensor, 0); // Auto WB mode
|
|
sensor->set_exposure_ctrl(sensor, 1); // Auto exposure
|
|
sensor->set_aec2(sensor, 0); // AEC DSP
|
|
sensor->set_ae_level(sensor, 0); // AE level
|
|
sensor->set_aec_value(sensor, 300); // AEC value
|
|
sensor->set_gain_ctrl(sensor, 1); // Auto gain
|
|
sensor->set_agc_gain(sensor, 0); // AGC gain
|
|
sensor->set_gainceiling(sensor, (gainceiling_t)0); // Gain ceiling
|
|
sensor->set_bpc(sensor, 0); // Black pixel correction
|
|
sensor->set_wpc(sensor, 1); // White pixel correction
|
|
sensor->set_raw_gma(sensor, 1); // Raw gamma
|
|
sensor->set_lenc(sensor, 1); // Lens correction
|
|
sensor->set_hmirror(sensor, 0); // Horizontal mirror
|
|
sensor->set_vflip(sensor, 0); // Vertical flip
|
|
sensor->set_dcw(sensor, 1); // Downsize EN
|
|
sensor->set_colorbar(sensor, 0); // Color bar test
|
|
|
|
Serial.printf("[Camera] Sensor PID: 0x%x\n", sensor->id.PID);
|
|
}
|
|
|
|
_initialized = true;
|
|
Serial.println("[Camera] Initialized successfully");
|
|
return true;
|
|
}
|
|
|
|
camera_fb_t* CameraModule::capture(int quality) {
|
|
if (!_initialized) {
|
|
_lastError = "Camera not initialized";
|
|
return nullptr;
|
|
}
|
|
|
|
// Temporarily change quality if specified
|
|
if (quality > 0 && quality != _currentQuality) {
|
|
sensor_t* sensor = esp_camera_sensor_get();
|
|
if (sensor) {
|
|
sensor->set_quality(sensor, quality);
|
|
}
|
|
}
|
|
|
|
// Capture frame
|
|
camera_fb_t* fb = esp_camera_fb_get();
|
|
|
|
// Restore quality
|
|
if (quality > 0 && quality != _currentQuality) {
|
|
sensor_t* sensor = esp_camera_sensor_get();
|
|
if (sensor) {
|
|
sensor->set_quality(sensor, _currentQuality);
|
|
}
|
|
}
|
|
|
|
if (!fb) {
|
|
_lastError = "Frame capture failed";
|
|
Serial.println("[Camera] Frame capture failed");
|
|
return nullptr;
|
|
}
|
|
|
|
Serial.printf("[Camera] Captured frame: %dx%d, %d bytes\n",
|
|
fb->width, fb->height, fb->len);
|
|
return fb;
|
|
}
|
|
|
|
void CameraModule::returnFrame(camera_fb_t* fb) {
|
|
if (fb) {
|
|
esp_camera_fb_return(fb);
|
|
}
|
|
}
|
|
|
|
bool CameraModule::setResolution(CameraResolution resolution) {
|
|
if (!_initialized) {
|
|
_lastError = "Camera not initialized";
|
|
return false;
|
|
}
|
|
|
|
sensor_t* sensor = esp_camera_sensor_get();
|
|
if (!sensor) {
|
|
_lastError = "Could not get sensor";
|
|
return false;
|
|
}
|
|
|
|
framesize_t framesize = resolutionToFramesize(resolution);
|
|
if (sensor->set_framesize(sensor, framesize) != 0) {
|
|
_lastError = "Failed to set resolution";
|
|
return false;
|
|
}
|
|
|
|
_currentResolution = resolution;
|
|
Serial.printf("[Camera] Resolution set to %s\n", getResolutionString());
|
|
return true;
|
|
}
|
|
|
|
bool CameraModule::setQuality(int quality) {
|
|
if (quality < 10 || quality > 63) {
|
|
_lastError = "Quality must be 10-63";
|
|
return false;
|
|
}
|
|
|
|
if (!_initialized) {
|
|
_currentQuality = quality;
|
|
return true;
|
|
}
|
|
|
|
sensor_t* sensor = esp_camera_sensor_get();
|
|
if (!sensor) {
|
|
_lastError = "Could not get sensor";
|
|
return false;
|
|
}
|
|
|
|
if (sensor->set_quality(sensor, quality) != 0) {
|
|
_lastError = "Failed to set quality";
|
|
return false;
|
|
}
|
|
|
|
_currentQuality = quality;
|
|
Serial.printf("[Camera] Quality set to %d\n", quality);
|
|
return true;
|
|
}
|
|
|
|
const char* CameraModule::getResolutionString() {
|
|
switch (_currentResolution) {
|
|
case RES_QVGA: return "QVGA";
|
|
case RES_VGA: return "VGA";
|
|
case RES_SVGA: return "SVGA";
|
|
case RES_XGA: return "XGA";
|
|
case RES_SXGA: return "SXGA";
|
|
case RES_UXGA: return "UXGA";
|
|
default: return "Unknown";
|
|
}
|
|
}
|
|
|
|
framesize_t CameraModule::resolutionToFramesize(CameraResolution res) {
|
|
switch (res) {
|
|
case RES_QVGA: return FRAMESIZE_QVGA;
|
|
case RES_VGA: return FRAMESIZE_VGA;
|
|
case RES_SVGA: return FRAMESIZE_SVGA;
|
|
case RES_XGA: return FRAMESIZE_XGA;
|
|
case RES_SXGA: return FRAMESIZE_SXGA;
|
|
case RES_UXGA: return FRAMESIZE_UXGA;
|
|
default: return FRAMESIZE_VGA;
|
|
}
|
|
}
|