This is the complete code, I haven’t had a close look at it but I suspect I’ve got some work to do with it. The calculation is done at line 65
#include "esp_camera.h"
#include "FS.h" // SD Card ESP32
#include "SD_MMC.h" // SD Card ESP32
#include "soc/soc.h" // Disable brownout problems
#include "soc/rtc_cntl_reg.h" // Disable brownout problems
#include "driver/rtc_io.h"
#define MINUTES_BETWEEN_PHOTOS 30
// Pin definition for CAMERA_MODEL_AI_THINKER
#define PWDN_GPIO_NUM 32
#define RESET_GPIO_NUM -1
#define XCLK_GPIO_NUM 0
#define SIOD_GPIO_NUM 26
#define SIOC_GPIO_NUM 27
#define Y9_GPIO_NUM 35
#define Y8_GPIO_NUM 34
#define Y7_GPIO_NUM 39
#define Y6_GPIO_NUM 36
#define Y5_GPIO_NUM 21
#define Y4_GPIO_NUM 19
#define Y3_GPIO_NUM 18
#define Y2_GPIO_NUM 5
#define VSYNC_GPIO_NUM 25
#define HREF_GPIO_NUM 23
#define PCLK_GPIO_NUM 22
// Flash LED pin
#define FLASH_LED_PIN 4
void setup() {
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); // Disable brownout detector
// Initialize serial
Serial.begin(115200);
while (!Serial) delay(100);
// Initialize SD Card BEFORE camera to reduce memory pressure
startMicroSD();
// Initialize camera
startCamera();
delay(2000); // Delay 2 seconds before first photo
}
void loop() {
// Keep a count of the number of photos we have taken
static int number = 0;
number++;
// Construct a filename that looks like "/photo_0001.jpg"
String filename = "/photo_";
if (number < 1000) filename += "0";
if (number < 100) filename += "0";
if (number < 10) filename += "0";
filename += number;
filename += ".jpg";
takePhoto(filename);
// Delay until the next photo
delay(MINUTES_BETWEEN_PHOTOS * 60 * 1000);
}
bool startMicroSD() {
Serial.print("Starting microSD... ");
// Pin 13 needs to be pulled-up
//
pinMode(13, OUTPUT);
digitalWrite(13, HIGH);
if (SD_MMC.begin("/sdcard", true)) {
Serial.println("OKAY");
return true;
} else {
Serial.println("FAILED");
return false;
}
}
void startCamera() {
// Turn off the flash
pinMode(FLASH_LED_PIN, OUTPUT);
digitalWrite(FLASH_LED_PIN, LOW);
// Camera configuration - REDUCED settings to prevent stack overflow
camera_config_t config;
config.ledc_channel = LEDC_CHANNEL_0;
config.ledc_timer = LEDC_TIMER_0;
config.pin_d0 = Y2_GPIO_NUM;
config.pin_d1 = Y3_GPIO_NUM;
config.pin_d2 = Y4_GPIO_NUM;
config.pin_d3 = Y5_GPIO_NUM;
config.pin_d4 = Y6_GPIO_NUM;
config.pin_d5 = Y7_GPIO_NUM;
config.pin_d6 = Y8_GPIO_NUM;
config.pin_d7 = Y9_GPIO_NUM;
config.pin_xclk = XCLK_GPIO_NUM;
config.pin_pclk = PCLK_GPIO_NUM;
config.pin_vsync = VSYNC_GPIO_NUM;
config.pin_href = HREF_GPIO_NUM;
config.pin_sccb_sda = SIOD_GPIO_NUM;
config.pin_sccb_scl = SIOC_GPIO_NUM;
config.pin_pwdn = PWDN_GPIO_NUM;
config.pin_reset = RESET_GPIO_NUM;
config.xclk_freq_hz = 20000000;
config.pixel_format = PIXFORMAT_JPEG;
// CRITICAL: Reduced frame size and quality to prevent stack overflow
if (psramFound()) {
Serial.println("PSRAM found");
config.frame_size = FRAMESIZE_XGA; // Reduced from UXGA to XGA
config.jpeg_quality = 12; // Increased from 10 to 12 (lower quality, smaller file)
config.fb_count = 1; // Reduced from 2 to 1 to save memory
} else {
Serial.println("PSRAM not found");
config.frame_size = FRAMESIZE_VGA; // Reduced from SVGA to VGA
config.jpeg_quality = 15; // Lower quality for boards without PSRAM
config.fb_count = 1;
}
// Additional memory-saving configurations
config.grab_mode = CAMERA_GRAB_LATEST; // Changed from WHEN_EMPTY to LATEST
// Initialize Camera
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x\n", err);
return;
}
Serial.println("Camera initialized successfully");
// Additional sensor settings to optimize for memory usage
sensor_t *s = esp_camera_sensor_get();
if (s != NULL) {
// Lower the resolution if needed
// s->set_framesize(s, FRAMESIZE_VGA);
// Optimize other settings
s->set_brightness(s, 0); // -2 to 2
s->set_contrast(s, 0); // -2 to 2
s->set_saturation(s, 0); // -2 to 2
s->set_special_effect(s, 0); // 0 to 6 (0 - No Effect)
s->set_whitebal(s, 1); // 0 = disable , 1 = enable
s->set_awb_gain(s, 1); // 0 = disable , 1 = enable
s->set_wb_mode(s, 0); // 0 to 4 - if awb_gain enabled
s->set_exposure_ctrl(s, 1); // 0 = disable , 1 = enable
s->set_aec2(s, 0); // 0 = disable , 1 = enable
s->set_ae_level(s, 0); // -2 to 2
s->set_aec_value(s, 300); // 0 to 1200
s->set_gain_ctrl(s, 1); // 0 = disable , 1 = enable
s->set_agc_gain(s, 0); // 0 to 30
s->set_gainceiling(s, (gainceiling_t)0); // 0 to 6
s->set_bpc(s, 0); // 0 = disable , 1 = enable
s->set_wpc(s, 1); // 0 = disable , 1 = enable
s->set_raw_gma(s, 1); // 0 = disable , 1 = enable
s->set_lenc(s, 1); // 0 = disable , 1 = enable
s->set_hmirror(s, 0); // 0 = disable , 1 = enable
s->set_vflip(s, 0); // 0 = disable , 1 = enable
s->set_dcw(s, 1); // 0 = disable , 1 = enable
s->set_colorbar(s, 0); // 0 = disable , 1 = enable
}
}
void takePhoto(String filename) {
digitalWrite(FLASH_LED_PIN, HIGH);
// Take Picture with Camera
Serial.println("Taking picture...");
camera_fb_t *fb = NULL;
// Try to take picture with retries
for (int retry = 0; retry < 3; retry++) {
fb = esp_camera_fb_get();
if (fb) break;
Serial.printf("Camera capture failed, retry %d\n", retry + 1);
delay(500);
}
digitalWrite(FLASH_LED_PIN, LOW);
if (!fb) {
Serial.println("Camera capture failed after retries");
return;
}
Serial.printf("Picture taken! Size: %zu bytes\n", fb->len);
// Save picture to SD card
fs::FS &fs = SD_MMC;
Serial.printf("Picture file name: %s\n", filename.c_str());
File file = fs.open(filename.c_str(), FILE_WRITE);
if (!file) {
Serial.println("Failed to open file in writing mode");
esp_camera_fb_return(fb);
return;
}
// Write file in chunks to prevent memory issues
size_t totalBytes = fb->len;
size_t bytesWritten = 0;
const size_t chunkSize = 1024; // Write in 1KB chunks
while (bytesWritten < totalBytes) {
size_t toWrite = min(chunkSize, totalBytes - bytesWritten);
size_t written = file.write(fb->buf + bytesWritten, toWrite);
if (written != toWrite) {
Serial.println("Write error occurred");
break;
}
bytesWritten += written;
}
file.close();
if (bytesWritten == totalBytes) {
Serial.printf("Saved file to path: %s (%zu bytes)\n", filename.c_str(), bytesWritten);
Serial.println("Picture saved successfully!");
} else {
Serial.printf("File write incomplete: %zu/%zu bytes\n", bytesWritten, totalBytes);
}
// Return the frame buffer immediately after use
esp_camera_fb_return(fb);
}