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