Page 1 of 1

Is it possible to take a photo and save it to an sdcard while also using an i2c device (DS3231 RTC)?

Posted: Mon Oct 11, 2021 4:43 pm
by FBMinis
Ive had success taking a photo and saving it to an SDCard but not when using an i2c connected RTC to take the photo at specific intervals. I havent been able to adjust the time, it always prints 21h00 when I do

Code: Select all

DateTime now = rtc.now();
  Serial.print("rtc time: "); Serial.print(now.hour(), DEC); Serial.print(" : "); Serial.println(now.minute(), DEC);
If I do not include the ability to save to SD, the code to set the time and set alarms works normally, as in another project of mine that takes a photo and sends to Telegram:

https://github.com/FBMinis/DS3231-Alarm ... legram-Bot

Is there an incompatibility between i2c and sdcard on the esp32cam?

Here's the code in attachment:

Code: Select all

/*
    http://asciiflow.com/

                 5V
                  +
                  |
                  +-----------+
                 +-+          |
            10K  | |     + +--+S
                 +++     |    |
                  |    G | +  |
     +------------+--------+--+  AO3401 P-CHANNEL MOSFET
     |                   |
     |                   | +
     |                   + +--+D
     |                        |
     |                 +------+------+
     |                 |      5V     |
     |                 |             |
     |        +---+----+3.3V         |
     |        |   |    |             |
  +----+----+  +++ +++   |             |
  |   SQW   |  | | | |10K|             |
  |         |  | | | |   |  ESP32CAM   |
  |  DS3231 |  +++ +++   |             |
  |         |   |   |    |             |
  |     SDA +---+--------+IO14         |
  |         |       |    |             |
  |     SCL +-------+----+IO15         |
  |         |            |             |
  +-----+---+            +----+--------+
      |                     |
      +-----------+---------+
                  |
                  +
               GROUND

  ESP32-Cam captures a photo and saves it to an sdcard at an interval defined by DS3231 alarms.

  Based on: https://RandomNerdTutorials.com/esp32-cam-take-photo-save-microsd-card

  RTC library used:
 
  https://github.com/adafruit/RTClib
  https://garrysblog.com/2020/07/05/using-the-ds3231-real-time-clock-alarm-with-the-adafruit-rtclib-library/
*/

#include <RTClib.h>
#include <Wire.h>
RTC_DS3231 rtc;
#define I2C_SDA 15
#define I2C_SCL 14
#define DS3231_I2C_ADDRESS 0x68
#define DS3231_CONTROL_REG 0x0E

#include "esp_camera.h"
#include "Arduino.h"
#include "FS.h"                // SD Card ESP32
#include "SD_MMC.h"            // SD Card ESP32
#include "soc/soc.h"           // Disable brownour problems
#include "soc/rtc_cntl_reg.h"  // Disable brownour problems
#include "driver/rtc_io.h"
#include <EEPROM.h>            // read and write from flash memory

// define the number of bytes you want to access
#define EEPROM_SIZE 1

// 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

int pictureNumber = 0;

void enableRTCAlarmsonBackupBattery() {
  // Enable Battery-Backed Square-Wave Enable on the DS3231 RTC module:
  // Bit 6 (Battery-Backed Square-Wave Enable) of DS3231_CONTROL_REG 0x0E, can be set to 1
  // When set to 1, it forces the wake-up alarms to occur when running the RTC from the back up battery alone.
  // [note: This bit is usually disabled (logic 0) when power is FIRST applied]
  // https://github.com/EKMallon/Utilities/blob/master/setTme/setTme.ino
  Wire.beginTransmission(DS3231_I2C_ADDRESS);      // Attention device at RTC address 0x68
  Wire.write(DS3231_CONTROL_REG);                  // move the memory pointer to CONTROL_REGister
  Wire.endTransmission();                          // complete the ‘move memory pointer’ transaction
  Wire.requestFrom(DS3231_I2C_ADDRESS, 1);         // request data from register
  byte resisterData = Wire.read();                 // byte from registerAddress
  bitSet(resisterData, 6);                         // Change bit 6 to a 1 to enable
  Wire.beginTransmission(DS3231_I2C_ADDRESS);      // Attention device at RTC address 0x68
  Wire.write(DS3231_CONTROL_REG);                  // target the CONTROL_REGister
  Wire.write(resisterData);                        // put changed byte back into CONTROL_REG
  Wire.endTransmission();
}

void setup() {
  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector

  Serial.begin(115200);
  //Serial.setDebugOutput(true);
  //Serial.println();

  Wire.begin(I2C_SDA, I2C_SCL);

  // initializing the rtc
  if (!rtc.begin()) {
    Serial.println("Couldn't find RTC!");
    Serial.flush();
    abort();
  }

  // Only needed if you cut the Vcc pin supplying power to the DS3231 chip to run clock from coincell
  enableRTCAlarmsonBackupBattery();

  if (rtc.lostPower()) {
    Serial.println("RTC lost power, let's set the time!");
    // When time needs to be set on a new device, or after a power loss, the
    // following line sets the RTC to the date & time this sketch was compiled
    // rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
    // This line sets the RTC with an explicit date & time, for example to set
    // January 21, 2014 at 3am you would call:
    // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
  }

  // When time needs to be re-set on a previously configured device, the
  // following line sets the RTC to the date & time this sketch was compiled
  // rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  // This line sets the RTC with an explicit date & time, for example to set
  // January 21, 2014 at 3am you would call:
  // rtc.adjust(DateTime(2014, 1, 21, 3, 0, 0));
  rtc.adjust(DateTime(2021, 10, 11, 16, 25, 0)); // used for troubleshooting, force set time at boot

  //we don't need the 32K Pin, so disable it
  rtc.disable32K();

  // stop oscillating signals at SQW Pin
  // otherwise setAlarm1 will fail
  rtc.writeSqwPinMode(DS3231_OFF);

  // turn off alarm 2 (in case it isn't off already)
  // again, this isn't done at reboot, so a previously set alarm could easily go overlooked
  rtc.disableAlarm(2);

  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_sscb_sda = SIOD_GPIO_NUM;
  config.pin_sscb_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;

  if (psramFound()) {
    config.frame_size = FRAMESIZE_UXGA; // FRAMESIZE_ + QVGA|CIF|VGA|SVGA|XGA|SXGA|UXGA
    config.jpeg_quality = 10;
    config.fb_count = 2;
  } else {
    config.frame_size = FRAMESIZE_SVGA;
    config.jpeg_quality = 12;
    config.fb_count = 1;
  }

  // Init Camera
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    //return;
  }

  //Serial.println("Starting SD Card");
  if (!SD_MMC.begin()) {
    Serial.println("SD Card Mount Failed");
    //return;
  }

  uint8_t cardType = SD_MMC.cardType();
  if (cardType == CARD_NONE) {
    Serial.println("No SD Card attached");
    //return;
  }

  // Take Picture with Camera
  camera_fb_t * fb = esp_camera_fb_get();
  if (!fb) {
    Serial.println("Camera capture failed");
    //return;
  }
  // initialize EEPROM with predefined size
  EEPROM.begin(EEPROM_SIZE);
  pictureNumber = EEPROM.read(0) + 1;

  // Path where new picture will be saved in SD Card
  String path = "/picture" + String(pictureNumber) + ".jpg";

  fs::FS &fs = SD_MMC;
  Serial.printf("Picture file name: %s\n", path.c_str());

  File file = fs.open(path.c_str(), FILE_WRITE);
  if (!file) {
    Serial.println("Failed to open file in writing mode");
  }
  else {
    file.write(fb->buf, fb->len); // payload (image), payload length
    Serial.printf("Saved file to path: %s\n", path.c_str());
    EEPROM.write(0, pictureNumber);
    EEPROM.commit();
  }
  file.close();
  esp_camera_fb_return(fb);

  // Set alarm to 10min from present time
  // If present hour is 20, 21, 22h, 23h, 0h, 1h, 2h,3h, 4h or 5h, alarm should happen after 1 hour
  DateTime now = rtc.now();
  Serial.print("rtc time: "); Serial.print(now.hour(), DEC); Serial.print(" : "); Serial.println(now.minute(), DEC);
  bool night_time = false;
  int night_hours[] = {20, 21, 22, 23, 0, 1, 2, 3, 4, 5};
  for (int index = 0; index < 10; index++) {
    if (night_hours[index] == now.hour()) {
      night_time = true;
      break;
    }
  }
  if (night_time) {
    if (!rtc.setAlarm1(
          rtc.now() + TimeSpan(0, 1, 0, 0),
          DS3231_A1_Hour //
        )) {
      Serial.println("Error, alarm wasn't set!");
    } else {
      Serial.println("Alarm will happen in X hours!");
    }
  } else {
    if (!rtc.setAlarm1(
          rtc.now() + TimeSpan(0, 0, 1, 0),
          DS3231_A1_Minute //
        )) {
      Serial.println("Error, alarm wasn't set!");
    } else {
      Serial.println("Alarm will happen in Y minutes!");
    }
  }

  Serial.print("turn on alarm: "); Serial.println(millis()); delay(10);
  // set alarm 1, 2 flag to false (so alarm 1, 2 didn't happen so far)
  // if not done, this easily leads to problems, as both register aren't reset on reboot/recompile
  // SQW becomes HIGH, P-MOSFET opens, power to the ESP2CAM is cut
  rtc.clearAlarm(1);
  rtc.clearAlarm(2);
}

void loop() {

}