I am using the DS3231 through i2C, it interacts with a p-channel mosfet. The DS3231 board we find online consumed 1.5mA while the system is off (until the alarm comes), but after removing some components I got 3uA: removed resistor blocks, eeprom chip and cut Pin 2 of the DS3231 (this way the chip works from the 2032 battery only).
Code: Select all
/*=========================================================
ESP32-Cam captures a photo and sends it to Telegram everytime the DS3231 alarm is tripped
Remove resistor blocks and eeprom chip from DS3231 board. Cut VCC pin (2). Place 10K pullups on GPIO14 and GPIO15.
3uA current consumption until alarm time, 100uA without cutting VCC pin.
http://asciiflow.com/
3.3V
+
|
+-----------+
+-+ | S
10k | | | +--+
+++ | |
| G | + |
+----------+------|-+--+ AO3401 p-channel mosfet
| |
| | +
| | +--+
| |D
| |
| +---+----------+
| 10K| | |
+-----+---+ +++ +++ +-----+---+
| SQW | | | | |10K| ESP32CAM|
| | | | | | | |
| DS3231 | +++ +++ | |
| | | | | |
| SDA +---+--------+IO14 |
| | | | |
| SCL +-------+----+IO15 |
| | | |
+-----+---+ +----+----+
| |
+-----------+---------+
|
+
GROUND
Everything about ESP32-CAM:
https://RandomNerdTutorials.com/telegram-esp32-cam-photo-arduino/
lowpower with DS3231:
https://thecavepearlproject.org/read-the-blog/ (ds3231 mod pictures)
https://sites.google.com/site/wayneholder/low-power-techniques-for-arduino
Libraries:
https://github.com/witnessmenow/Universal-Arduino-Telegram-Bot
https://github.com/adafruit/RTClib
IMPORTANT!!!
- Select Board "AI Thinker ESP32-CAM"
- GPIO 0 must be connected to GND to upload a sketch
- After connecting GPIO 0 to GND, press the ESP32-CAM on-board RESET button to put your board in flashing mode
=========================================================*/
#include "Arduino.h"
#include <RTClib.h>
RTC_DS3231 rtc;
#define DS3231_I2C_ADDRESS 0x68
#define DS3231_CONTROL_REG 0x0E
#include <Wire.h>
#define I2C_SDA 14
#define I2C_SDL 15
#include "esp_camera.h"
#include "driver/rtc_io.h"
#include "soc/soc.h" // Disable brownout problems
#include "soc/rtc_cntl_reg.h" // Disable brownout problems
// Wifi and Telegram stuff
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include <UniversalTelegramBot.h>
#include <ArduinoJson.h>
const char* ssid = "0000";
const char* password = "0000";
String BOTtoken = "0000"; // your Bot Token (Get from Botfather)
String CHAT_ID = "0000"; // Use @myidbot to find chat ID, individual or in group, then start bot
WiFiClientSecure clientTCP;
UniversalTelegramBot bot(BOTtoken, clientTCP);
// 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
void configInitCamera() {
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;
//init with high specs to pre-allocate larger buffers
if (psramFound()) {
config.frame_size = FRAMESIZE_UXGA;
config.jpeg_quality = 10; //0-63 lower number means higher quality
config.fb_count = 2;
} else {
config.frame_size = FRAMESIZE_SVGA;
config.jpeg_quality = 12; //0-63 lower number means higher quality
config.fb_count = 1;
}
// camera init
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf("Camera init failed with error 0x%x", err);
delay(1000);
ESP.restart();
}
// Drop down frame size for higher initial frame rate
sensor_t * s = esp_camera_sensor_get();
s->set_framesize(s, FRAMESIZE_UXGA); // UXGA|SXGA|XGA|SVGA|VGA|CIF|QVGA|HQVGA|QQVGA
s->set_whitebal(s, 1); // 0 = disable , 1 = enable
s->set_awb_gain(s, 1); // 0 = disable , 1 = enable
s->set_wb_mode(s, 2); // 0 to 4 - if awb_gain enabled (0 - Auto, 1 - Sunny, 2 - Cloudy, 3 - Office, 4 - Home)
//s->set_exposure_ctrl(s, 1); // 0 = disable , 1 = enable
//s->set_aec2(s, 0); // 0 = disable , 1 = enable
//s->set_ae_level(s, 2); // -2 to 2
//s->set_aec_value(s, 1200); // 0 to 1200
s->set_gain_ctrl(s, 0); // 0 = disable , 1 = enable
s->set_agc_gain(s, 0); // 0 to 30
s->set_gainceiling(s, (gainceiling_t)6); // 0 to 6
s->set_bpc(s, 1); // 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, 0); // 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, 0); // 0 = disable , 1 = enable
s->set_colorbar(s, 0); // 0 = disable , 1 = enable
}
String sendPhotoTelegram() {
const char* myDomain = "api.telegram.org";
String getAll = "";
String getBody = "";
camera_fb_t * fb = NULL;
fb = esp_camera_fb_get();
if (!fb) {
Serial.println("Camera capture failed");
delay(1000);
ESP.restart();
return "Camera capture failed";
}
Serial.println("Connect to " + String(myDomain));
if (clientTCP.connect(myDomain, 443)) {
Serial.println("Connection successful");
String head = "--RandomNerdTutorials\r\nContent-Disposition: form-data; name=\"chat_id\"; \r\n\r\n" + CHAT_ID + "\r\n--RandomNerdTutorials\r\nContent-Disposition: form-data; name=\"photo\"; filename=\"esp32-cam.jpg\"\r\nContent-Type: image/jpeg\r\n\r\n";
String tail = "\r\n--RandomNerdTutorials--\r\n";
uint16_t imageLen = fb->len;
uint16_t extraLen = head.length() + tail.length();
uint16_t totalLen = imageLen + extraLen;
clientTCP.println("POST /bot" + BOTtoken + "/sendPhoto HTTP/1.1");
clientTCP.println("Host: " + String(myDomain));
clientTCP.println("Content-Length: " + String(totalLen));
clientTCP.println("Content-Type: multipart/form-data; boundary=RandomNerdTutorials");
clientTCP.println();
clientTCP.print(head);
uint8_t *fbBuf = fb->buf;
size_t fbLen = fb->len;
for (size_t n = 0; n < fbLen; n = n + 1024) {
if (n + 1024 < fbLen) {
clientTCP.write(fbBuf, 1024);
fbBuf += 1024;
}
else if (fbLen % 1024 > 0) {
size_t remainder = fbLen % 1024;
clientTCP.write(fbBuf, remainder);
}
}
clientTCP.print(tail);
esp_camera_fb_return(fb);
int waitTime = 10000; // timeout 10 seconds
long startTimer = millis();
boolean state = false;
while ((startTimer + waitTime) > millis()) {
Serial.print(".");
delay(100);
while (clientTCP.available()) {
char c = clientTCP.read();
if (state == true) getBody += String(c);
if (c == '\n') {
if (getAll.length() == 0) state = true;
getAll = "";
}
else if (c != '\r')
getAll += String(c);
startTimer = millis();
}
if (getBody.length() > 0) break;
}
clientTCP.stop();
Serial.println(getBody);
}
else {
getBody = "Connected to api.telegram.org failed.";
Serial.println("Connected to api.telegram.org failed.");
}
return getBody;
}
void setup() {
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
//Serial.begin(9600);
Serial.setDebugOutput(true);
Wire.begin(I2C_SDA, I2C_SDL);
// initializing the rtc
if (!rtc.begin()) {
Serial.println("Couldn't find RTC!");
Serial.flush();
abort();
}
// clearClockTrigger();
enableRTCAlarmsonBackupBattery(); // only needed if you cut the Vcc pin supplying power to the DS3231 chip to run clock from coincell
if (rtc.lostPower()) {
// this will adjust to the date and time at compilation
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
rtc.disable32K(); // we don't need the 32K Pin, so disable it
// 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
// rtc.clearAlarm(1);
// rtc.clearAlarm(2);
rtc.writeSqwPinMode(DS3231_OFF); // stop oscillating signals at SQW Pin otherwise setAlarm1 will fail
// 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);
configInitCamera(); // Config and init the camera
// Connect to Wi-Fi
WiFi.mode(WIFI_STA);
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(500);
}
Serial.println();
Serial.print("ESP32-CAM IP Address: ");
Serial.println(WiFi.localIP());
sendPhotoTelegram(); // Capture the image and send it to Telegram
// schedule an alarm 60 seconds in the future
if (!rtc.setAlarm1(
rtc.now() + TimeSpan(60),
DS3231_A1_Second // this mode triggers the alarm when the seconds match. See Doxygen for other options
)) {
Serial.println("Error, alarm wasn't set!");
} else {
Serial.println("Alarm will happen in 60 seconds!");
}
// print current time
char date[10] = "hh:mm:ss";
rtc.now().toString(date);
Serial.print("current time: ");
Serial.println(date);
Serial.println("be right back");
// 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);
/* // capture a frame (test)
camera_fb_t * fb = NULL;
fb = esp_camera_fb_get();
if(!fb) {
Serial.println("Camera capture failed");
delay(1000);
ESP.restart();
return "Camera capture failed";
}
// do something with the photo
// end of do something with the photo
esp_camera_fb_return(fb); */
/* // wake up from external source (LOW pulse on GPIO13)
esp_sleep_enable_ext0_wakeup(GPIO_NUM_13, 0);
Serial.println("Going to sleep now");
delay(1000);
esp_deep_sleep_start();
Serial.println("This will never be printed"); */
}
void loop() {
}
/* static uint8_t read_i2c_register(uint8_t addr, uint8_t reg)
{
Wire.beginTransmission(addr);
Wire.write((byte)reg);
Wire.endTransmission();
Wire.requestFrom(addr, (byte)1);
return Wire.read();
} */
// 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
void enableRTCAlarmsonBackupBattery() {
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();
}
/* // clearClockTrigger function needed to acheive low sleep currents with DS3231 Vcc Pin cut
// from http://forum.arduino.cc/index.php?topic=109062.0
void clearClockTrigger() {
Wire.beginTransmission(0x68); //Tell devices on the bus we are talking to the DS3231
Wire.write(0x0F); //Tell the device which address we want to read or write
Wire.endTransmission(); //Before you can write to and clear the alarm flag
Wire.requestFrom(0x68,1); //you have to read the flag first! Read one byte
Wire.read(); //Read one byte - we are not interest in actually using it
Wire.beginTransmission(0x68); //Tell devices on the bus we are talking to the DS3231
Wire.write(0x0F); //Status Register: Bit 3: zero disables 32kHz, Bit 7: zero enables the main oscilator
Wire.write(0b00000000); //Bit1: zero clears Alarm 2 Flag (A2F), Bit 0: zero clears Alarm 1 Flag (A1F)
Wire.endTransmission();
}
*/