No issue - feature request for ESP32-CAM
Hi Tolentino,
I have been using your library for almost 3 years, thank you for sharing your work. I modified one of the examples so my battery powered ESP32CAMs turn ON and send a photo, either using PIR as a trigger or a DS3231 to define the interval of time. One of them as sent me more than 3000 photos since I last charge the battery, the connection to Telegram works perfectly.
I would like the photo to include a caption, which would be a variable with a timestamp, previously stored in LittleFS. How do you add a caption to a photo?
The following code is what I use to send a photo to my Telegram account when I power ON the device:
//ESP32-CAM sends a photo to Telegram when power is applied
/*----------------------------------------------------------------------------------------------------------------------------
Used: E:\arduino-1.8.19\portable\packages\esp32\hardware\esp32\2.0.14\libraries\WiFi
Not used: E:\arduino-1.8.19\libraries\WiFi
Using library WiFi at version 2.0.0 in folder: E:\arduino-1.8.19\portable\packages\esp32\hardware\esp32\2.0.14\libraries\WiFi
Using library AsyncTelegram2 at version 2.2.1 in folder: E:\arduino-1.8.19\portable\sketchbook\libraries\AsyncTelegram2
Using library ArduinoJson at version 6.21.3 in folder: E:\arduino-1.8.19\portable\sketchbook\libraries\ArduinoJson
Using library FS at version 2.0.0 in folder: E:\arduino-1.8.19\portable\packages\esp32\hardware\esp32\2.0.14\libraries\FS
Using library WiFiClientSecure at version 2.0.0 in folder: E:\arduino-1.8.19\portable\packages\esp32\hardware\esp32\2.0.14\libraries\WiFiClientSecure
"E:\\arduino-1.8.19\\portable\\packages\\esp32\\tools\\xtensa-esp32-elf-gcc\\esp-2021r2-patch5-8.4.0/bin/xtensa-esp32-elf-size" -A "C:\\Users\\user\\AppData\\Local\\Temp\\arduino_build_583398/sendPhotoTelegramStartup.ino.elf"
Sketch uses 1014089 bytes (32%) of program storage space. Maximum is 3145728 bytes.
Global variables use 49984 bytes (15%) of dynamic memory, leaving 277696 bytes for local variables. Maximum is 327680 bytes.
----------------------------------------------------------------------------------------------------------------------------*/
#include <WiFi.h>
#include <AsyncTelegram2.h> //https://github.com/cotestatnt/AsyncTelegram2/tree/2.3.0
#include "esp_camera.h"
#include "soc/soc.h" // Brownout error fix
#include "soc/rtc_cntl_reg.h" // Brownout error fix
#include <WiFiClientSecure.h>
WiFiClientSecure secure_client;
const char* ssid = "blabla"; // SSID WiFi network
const char* password = "blablabla"; // Password WiFi network
IPAddress local_IP(192, 168, 1, 80);
IPAddress gateway(192, 168, 1, 254);
IPAddress subnet(255, 255, 0, 0);
IPAddress primaryDNS(8, 8, 8, 8); //optional
IPAddress secondaryDNS(8, 8, 4, 4); //optional
const char* token = "blablabla"; //Telegram token
// Check the userid with the help of bot @JsonDumpBot or @getidsbot (work also with groups)
// https://t.me/JsonDumpBot or https://t.me/getidsbot
int64_t userid = 789512150; //replace with your userid
int wifi_start_counter = 0;
// Timezone definition to get properly time from NTP server
#define MYTZ "WET0WEST,M3.5.0/1,M10.5.0" // Portugal
AsyncTelegram2 myBot(secure_client);
#define LED_PIN 33 // Pin 33 on ESP32CAM
// 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
static camera_config_t camera_config = {
.pin_pwdn = PWDN_GPIO_NUM,
.pin_reset = RESET_GPIO_NUM,
.pin_xclk = XCLK_GPIO_NUM,
.pin_sscb_sda = SIOD_GPIO_NUM,
.pin_sscb_scl = SIOC_GPIO_NUM,
.pin_d7 = Y9_GPIO_NUM,
.pin_d6 = Y8_GPIO_NUM,
.pin_d5 = Y7_GPIO_NUM,
.pin_d4 = Y6_GPIO_NUM,
.pin_d3 = Y5_GPIO_NUM,
.pin_d2 = Y4_GPIO_NUM,
.pin_d1 = Y3_GPIO_NUM,
.pin_d0 = Y2_GPIO_NUM,
.pin_vsync = VSYNC_GPIO_NUM,
.pin_href = HREF_GPIO_NUM,
.pin_pclk = PCLK_GPIO_NUM,
.xclk_freq_hz = 20000000,
.ledc_timer = LEDC_TIMER_0,
.ledc_channel = LEDC_CHANNEL_0,
.pixel_format = PIXFORMAT_JPEG, /* PIXFORMAT_RGB565, // for face detection/recognition */
.frame_size = FRAMESIZE_VGA,
.jpeg_quality = 12,
.fb_count = 1,
.fb_location = CAMERA_FB_IN_PSRAM,
.grab_mode = CAMERA_GRAB_WHEN_EMPTY,
#if CONFIG_CAMERA_CONVERTER_ENABLED
.conv_mode = CONV_DISABLE, /*!< RGB<->YUV Conversion mode */
#endif
//.sccb_i2c_port = 1 /*!< If pin_sccb_sda is -1, use the already configured I2C bus by number */
};
camera_fb_t * fb = NULL;
static esp_err_t init_camera() {
// Best picture quality, but first frame requestes get lost sometimes (comment / uncomment to try)
if (psramFound()) {
Serial.println("PSRAM found");
camera_config.fb_count = 2;
camera_config.grab_mode = CAMERA_GRAB_LATEST;
}
//initialize the camera
Serial.print("Camera init... ");
esp_err_t err = esp_camera_init(&camera_config);
if (err != ESP_OK) {
delay(100); // need a delay here or the next serial o/p gets missed
Serial.printf("\n\nCRITICAL FAILURE: Camera sensor failed to initialise.\n\n");
Serial.printf("A full (hard, power off/on) reboot will probably be needed to recover from this.\n");
return err;
} else {
Serial.println("succeeded");
// Get a reference to the sensor
sensor_t* s = esp_camera_sensor_get();
// Dump camera module, warn for unsupported modules.
switch (s->id.PID) {
case OV9650_PID: Serial.println("WARNING: OV9650 camera module is not properly supported, will fallback to OV2640 operation"); break;
case OV7725_PID: Serial.println("WARNING: OV7725 camera module is not properly supported, will fallback to OV2640 operation"); break;
case OV2640_PID: Serial.println("OV2640 camera module detected"); break;
case OV3660_PID: Serial.println("OV3660 camera module detected"); break;
default: Serial.println("WARNING: Camera module is unknown and not properly supported, will fallback to OV2640 operation");
}
// Adjust sensor settings
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_hmirror(s, 1); // 0 = disable , 1 = enable
s->set_vflip(s, 1); // 0 = disable , 1 = enable
}
return ESP_OK;
}
size_t sendPicture(int64_t userid) {
// Take Picture with Camera;
Serial.println("Camera capture requested");
// Discard a few frames
for (int j = 0; j < 4; j++) {
camera_fb_t* newfb = esp_camera_fb_get();
if (!newfb) {
Serial.println("newfb capture failed");
} else {
//Serial.print("Pic, len="); Serial.print(newfb->len);
//Serial.printf(", new fb %X\n", (long)newfb->buf);
esp_camera_fb_return(newfb);
delay(100);
}
}
// Take Picture with Camera
camera_fb_t* fb = esp_camera_fb_get();
if (!fb) {
Serial.println("fb capture failed");
return 0;
}
size_t len = fb->len;
// Serial.print("len:");Serial.println(len);//testing
myBot.sendPhoto(userid, fb->buf, fb->len);
// Clear buffer
Serial.print("Frame captured after (sec): ");
Serial.println(millis() * 0.001);
esp_camera_fb_return(fb);
return len;
}
void init_wifi() {
// Optionally connect with a specific IP
if (!WiFi.config(local_IP, gateway, subnet, primaryDNS, secondaryDNS)) {
Serial.println("STA Failed to configure");
}
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
wifi_start_counter++;
if (wifi_start_counter >= 60) {
break; //
}
}
Serial.println("");
Serial.println("WiFi connected!");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
Serial.print("ESP Mac Address: ");
Serial.println(WiFi.macAddress());
Serial.print("Subnet Mask: ");
Serial.println(WiFi.subnetMask());
Serial.print("Gateway IP: ");
Serial.println(WiFi.gatewayIP());
Serial.print("DNS: ");
Serial.println(WiFi.dnsIP());
Serial.print("Connected after (sec): ");
Serial.println(millis() * 0.001);
}
void blinkLED(int numBlinks, int blinkInterval) {
for (int i = 0; i < numBlinks; i++) {
digitalWrite(LED_PIN, LOW); // Turn on the LED
delay(blinkInterval);
digitalWrite(LED_PIN, HIGH); // Turn off the LED
delay(blinkInterval);
}
}
void sendToTelegram() {
// Init the camera module (accordind the camera_config_t defined)
init_camera();
// Sync time with NTP
configTzTime(MYTZ, "time.google.com", "time.windows.com", "pool.ntp.org");
secure_client.setCACert(telegram_cert);
// I couldn't get this to work
// myBot.addSentCallback([](bool sent) {
// const char* res = sent ? "Picture delivered!" : "Error! Picture NOT delivered";
// if (!sent)
// myBot.sendTo(userid, res);
// }, 3000);
// Set the Telegram bot properies
myBot.setUpdateTime(1000);
myBot.setTelegramToken(token);
// Check if all things are ok
Serial.print("\nTest Telegram connection... ");
myBot.begin() ? Serial.println("OK") : Serial.println("NOK");
Serial.println("\nSending Photo from CAM");
if (sendPicture(userid))
Serial.println("Picture sent successfull");
else
myBot.sendTo(userid, "Error, picture not sent.");
}
void setup() {
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); // disable brownout detector
Serial.begin(115200);
Serial.println();
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, HIGH);
blinkLED(2, 100); // blink red led two times on power on
init_wifi();
sendToTelegram();
blinkLED(3, 100); // blink red led three times when picture has been sent
}
void loop() {
}
Hi @FBMinis
You are sending photo using this method:
inline bool sendPhoto(int64_t chat_id, uint8_t *data, size_t size, const char *caption = nullptr)
As you can see, caption is already supported but actually null as default. So you can prepare your timestamp string just before sending the message and include it.
char caption_str[20];
time_t now = time(nullptr);
tInfo = *localtime(&now);
strftime(caption_str, sizeof(caption_str), "%d/%m/%Y %H:%M:%S", &tInfo);
myBot.sendPhoto(userid, fb->buf, fb->len, caption_str);
Working perfectly, thank you!
char caption_str[20];
struct tm tInfo;
time_t now = time(nullptr);
tInfo = *localtime(&now);
strftime(caption_str, sizeof(caption_str), "%d/%m/%Y %H:%M:%S", &tInfo);
myBot.sendPhoto(userid, fb->buf, fb->len, caption_str);