Maths problem dividing 30 minutes into 30 seconds

I’ve got a sample sketch for ESP32-CAM with ESP32-CAM-MB that uses:-

#define MINUTES_BETWEEN_PHOTOS 30

to take a photo every half hour.

I want to increase the frequency to somewhere around 1 per second, my thought is to repeatedly halve the interval until it can produce a smooth motion of breaks.

Its been a decade since I did any real coding and my references are still in storage until I can put the hard word on one of my grandsons to help me find the right carton.

mick

You'll have to post that sketch.

30 is minutes in some context where you want 30/1800 minutes (1 second).

But it is integer maths, so you'll need to deal in some other units. How will only be ckear if we can see the code,

a7

1 Like

That will depend entirely on how your program uses that value, not on how you want to modify it.

1 Like

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

}

The sketch is programmed using C++, so you have to be consider the difference between integer math and "regular" floating point. For example 7 / 2 is 3, not three-and-a-half.

Partly for this reason, the time argument for delay is in milliseconds. So you can divide by 2 for a while before you run into too much trouble, although it will often not divide exactly in half -- although that may not make a practical difference.

How would this work? When does "repeatedly" happen? Are you recompiling and uploading each time? Or is it calculating by itself?

1 Like

1 per second is

  delay(1000);
1 Like

Thanks, that was my initial thought but it seemed too easy

You need to check the remainder of the sketch to see if there are other delays that might interfere with using a simple delay() for timing. There is a 500mS delay in the takePhoto() function, for example, although that is only if the camera fails to take a photo, and storing to an SD card can take a significant portion of a second.

1 Like

I have had a look and not found any, but I will look through a couple of times more. I haven’t cut any code or even looked at any since yahoo chat died.

My initial thought was to edit either the //*define MINUTES_BETWEEN_PHOTOS 30; or //*delay(MINUTES_BETWEEN_PHOTOS * 60 * 1000); to an integer that was human friendly rather than use a combination of variables and values that hide the real value.

mick

Thanks

1 per second is

  delay(1000);

Is the value I expected to reach by trial & error. Its too long since I have coded anything beyond the most trivial.

mick

If you change #define MINUTES_BETWEEN_PHOTOS 30 to #define SECONDS_BETWEEN_PHOTOS 30 and change delay(MINUTES_BETWEEN_PHOTOS * 60 * 1000); to delay(SECONDS_BETWEEN_PHOTOS * 1000); you will have a 30 second delay. I'm sure that you can figure out how to change it to 1 second.

This will make that you don´t have to search for that delay.

to be sure your timing is right it would be better to use millis() for timing, as in this example.

https://docs.arduino.cc/built-in-examples/digital/BlinkWithoutDelay/

and to be on the safe side (although it does not matter much on ESP32 for typical values of the delay), you should force this to unsigned long maths with

delay(SECONDS_BETWEEN_PHOTOS * 1000ul);

2 Likes

An example of a non-blocking timer using millis()...

unsigned long timer, timeout = 1000; // time between photos

void setup() {
  Serial.begin(115200); // start serial comm
}

void loop() {
  if (millis() - timer >= timeout) { // compare current time to time between photos
//  if (millis() - timer > timeout) { // edited... see post 16
    timer = millis(); // reset latest time
    takePhoto(); // do something
  }
}

void takePhoto() {
  Serial.print("snap "); // something
}

May be >= otherwise you’ll wait possibly 1 extra millisecond :innocent::smiley:

2 Likes

I was going to manually edit the value of MINUTES_BETWEEN_PHOTOS until I reached the lowest value that gave a reliable performance. I know there are much better ways to do it but I’m struggling to remember them after a few years not writing or editing any code.

I have received a couple of good answers to this post, now I have to pick the one that feels best and implement it.

Using a number with a decimal point, including ending with .0 for a whole number, will do the calculation accurately -- for a very specific meaning of that word. For example, it is impossible to represent exactly 0.1; the closest you get is approximately 0.100000000000000005551, and that difference occasionally matters. But at least there's no confounding overflow, at least not when dealing with "normal-sized" numbers.

So 0.1 MINUTES will be almost exactly six seconds for the delay by itself. If that's too hard to use, as suggested, you can switch to SECONDS for the magic constant, and drop the * 60 from the calculation.

1 Like

I can honestly say I never would have thought of that.

a7

I’m starting to think my best option is to get rid of

#define MINUTES_BETWEEN_PHOTOS 30/1800

and change

delay(MINUTES_BETWEEN_PHOTOS * 60 * 1000);

to a hard coded integer such as

delay(1000);

I’ve

I need find a deference for delay.

If I find out who stole my copy of “The C Programming Language” I’ll put them through the car shredder.