AsyncTelegram2 icon indicating copy to clipboard operation
AsyncTelegram2 copied to clipboard

No issue - feature request for ESP32-CAM

Open FBMinis opened this issue 2 years ago • 2 comments

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() {

}

FBMinis avatar Jan 28 '24 13:01 FBMinis

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

cotestatnt avatar Jan 29 '24 06:01 cotestatnt

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

FBMinis avatar Jan 29 '24 14:01 FBMinis