cotestatnt / AsyncTelegram2

Powerful, flexible and secure Arduino Telegram BOT library. Hardware independent, it can be used with any MCU capable of handling an SSL connection.
MIT License
83 stars 25 forks source link

No issue - feature request for ESP32-CAM #135

Closed FBMinis closed 4 weeks ago

FBMinis commented 5 months ago

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

}
cotestatnt commented 5 months ago

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);
FBMinis commented 5 months ago

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