esp32-claude-robbie/esp32_firmware/src/camera.cpp

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;
}
}