Problem with ESP32-cam pir motion connected to Telegram

No @noobide, I mean the "picture grabbed from CAM" upload speed to Telegram server.

When you have to send a picture from your bot, how much time is necessary?
I'm hardly working to my library in order to increase this speed, so i would like to compare with others.
Actually I've reached about 70-90 Kb/second

You have to open the webcameraserver sketch first. You go Files --> Examples --> ESP32 --> Camera --> CameraWebServer. This will open 4 tabs, camera_pins.h included. Then put "my" sketch in the main tab, replacing the one already there obviously. And now you can upload the code to your ESP !

The LED already turns on when taking a photo. And by adding those lines I wanted the LED also to stay on for a certain amount of time when the motion is detected. However it actually turns on but never turns off .. You have to do it manually with the /Flash command.

About this line
/*else { bot.sendMessage(chatId, "I don't understand, sorry. Refer to the commands I showed you above.", ""); }*/

I want the telegram bot to return an error message when the command is wrong. But with this line, it returns the error message even with the right commands, I mean /PIROn, /Flash etc..

How m I supposed to know this ? Maybe you can lead me on how to find out ? It actually takes about 2-3 seconds to receive the photo !

Btw, I tried to use your library last time, though I gave up because when I compiled the code it showed a lot of errors, the sketch was based on UniversalTelegramBot or smth and had to be changed

simply put something like this:

   uint32_t t1 = millis();
   // The code for sending the message       
   Serial.printf("Total upload time %lu ms\n", millis() - t1 );

Yes, of course.
If you would like try my library, I suggest you starting from the included examples.

Actually, inspired from your post, I'm preparing a full working example with PIR motion external trigger.
It's almost done, I will upload all updates on Github soon (probably this evening).

1 Like

Here is what I get on the serial monitor

Handle New Messages: 1
New photo  request
Preparing photo
Connect to
Connection successful
Total upload time 0 ms
...{"ok":true,"result":{"message_id":340,"from":{"id":1712454  etc ...

That's good stuff, I'll take a look at this ! God bless people like you !
You should also add the video stream part ! I didn't find a sktech like this anywhere

Okay here is the final code, all the issues are fixed everything's working well for me.

First of all You go Files --> Examples --> ESP32 --> Camera --> CameraWebServer. Then copy this code in the main tab :

#include "esp_camera.h"
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"
#include "esp_camera.h"
#include <UniversalTelegramBot.h>
#include <ArduinoJson.h>
#include <Wire.h>


#include "camera_pins.h"

const char* ssid = "-----------";
const char* password = "---------";

String chatId = "--------";

// Initialize Telegram BOT
String BOTtoken = "-------";

bool sendPhoto = false;

WiFiClientSecure clientTCP;

UniversalTelegramBot bot(BOTtoken, clientTCP);

#define LED 4
#define PIR 2
bool flashState = LOW;

// Motion Sensor
bool motionDetected = false;
bool PirControler = 0;

int botRequestDelay = 1000;   // mean time between scan messages
long lastTimeBotRan;     // last time messages' scan has been done

void startCameraServer();

void handleNewMessages(int numNewMessages);
String sendPhotoTelegram();

// Indicates when motion is detected
static void IRAM_ATTR detectsMovement(void * arg){
  //Serial.println("MOTION DETECTED!!!");
  motionDetected = true;

void setup() {

  Serial.print("Connecting to ");
  WiFi.begin(ssid, password);
  clientTCP.setCACert(TELEGRAM_CERTIFICATE_ROOT); // Add root certificate for
  while (WiFi.status() != WL_CONNECTED) {

  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 PSRAM IC present, init with UXGA resolution and higher JPEG quality
  //                      for larger pre-allocated frame buffer.
    config.frame_size = FRAMESIZE_UXGA;
    config.jpeg_quality = 10;
    config.fb_count = 2;
  } else {
    config.frame_size = FRAMESIZE_SVGA;
    config.jpeg_quality = 12;
    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);

  sensor_t * s = esp_camera_sensor_get();
  // initial sensors are flipped vertically and colors are a bit saturated
  if (s->id.PID == OV3660_PID) {
    s->set_vflip(s, 1); // flip it back
    s->set_brightness(s, 1); // up the brightness just a bit
    s->set_saturation(s, -2); // lower the saturation
  // drop down frame size for higher initial frame rate
  s->set_framesize(s, FRAMESIZE_QVGA);

  err = gpio_isr_handler_add(GPIO_NUM_13, &detectsMovement, (void *) 13);  
  if (err != ESP_OK){
    Serial.printf("handler add failed with error 0x%x \r\n", err); 
  err = gpio_set_intr_type(GPIO_NUM_13, GPIO_INTR_POSEDGE);
  if (err != ESP_OK){
    Serial.printf("set intr type failed with error 0x%x \r\n", err);


  Serial.print("Camera Ready! Use 'http://");
  Serial.println("' to connect");
  pinMode(LED, OUTPUT);
  digitalWrite(LED, flashState);

void loop() {
  if (sendPhoto){
    Serial.println("Preparing photo");
    sendPhoto = false; 
    bot.sendMessage(chatId, "Motion detected!!", "");
    Serial.println("Motion Detected");
    motionDetected = false;
  if (millis() > lastTimeBotRan + botRequestDelay){
    int numNewMessages = bot.getUpdates(bot.last_message_received + 1);
    while (numNewMessages){
      Serial.println("got response");
      numNewMessages = bot.getUpdates(bot.last_message_received + 1);
    lastTimeBotRan = millis();

  if (PirControler){
    if(digitalRead(PIR) == 1){
      digitalWrite(LED, HIGH);
      delay (1000);
      Serial.print("Motion Detected,  Value = ");
      String motion = "Motion detected !\n";
      motion += "You will receive a photo now.\n";
      bot.sendMessage(chatId, motion, "");
  if (PirControler){
    if(digitalRead(PIR) == 0) {
      digitalWrite(LED, LOW);
      delay (2000);

String sendPhotoTelegram(){
  const char* myDomain = "";
  String getAll = "";
  String getBody = "";

  camera_fb_t * fb = NULL;
  fb = esp_camera_fb_get();  
  if(!fb) {
    Serial.println("Camera capture failed");
    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" + chatId + "\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;    
    uint32_t t1 = millis();
    Serial.printf("Total upload time %lu ms\n", millis() - t1 );    
    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");
    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);
    int waitTime = 10000;   // timeout 10 seconds
    long startTimer = millis();
    boolean state = false;
    while ((startTimer + waitTime) > millis()){
      while (clientTCP.available()) {
        char c =;
        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;
  else {
    getBody="Connected to failed.";
    Serial.println("Connected to failed.");
  return getBody;

void handleNewMessages(int numNewMessages){
  Serial.print("Handle New Messages: ");

  for (int i = 0; i < numNewMessages; i++){
    // Chat id of the requester
    String chat_id = String(bot.messages[i].chat_id);
    if (chat_id != chatId){
      bot.sendMessage(chat_id, "Unauthorized user", "");
    // Print the received message
    String text = bot.messages[i].text;

    String fromName = bot.messages[i].from_name;

    if (text == "/FlashOn") {
      flashState = HIGH;
      digitalWrite(LED, flashState);

    else if (text == "/FlashOff") {
      flashState = LOW;
      digitalWrite(LED, flashState);
    else if (text == "/Photo") {
      sendPhoto = true;
      Serial.println("New photo request");
    else if (text == "/PIROn"){
      PirControler = 1;
      bot.sendMessage(chatId, "PIR Sensor is ON, you will get a notification if a motion is detected.", "");

    else if (text == "/PIROff"){
      PirControler = 0;
      bot.sendMessage(chatId, "PIR sensor is OFF, you will no longer receive any notification.", "");

    else if (text == "/start"){
      String welcome = "Hi sir, here are the commands I can execute for you :\n";
      welcome += "/Photo : Take a new photo.\n";
      welcome += "/FlashOn : Turns flash LED ON.\n";
      welcome += "/FlashOff : Turns flash LED Off.\n";
      welcome += "/PIROn : Activate your PIR sensor.\n";
      welcome += "/PIROff : Shut your PIR sensor down.\n";
      // welcome += "/readings : request sensor readings\n\n";
      welcome += "You'll receive a photo whenever motion is detected.\n";
      bot.sendMessage(chatId, welcome, "Markdown");
    else {
      bot.sendMessage(chatId, "I don't understand, sorry. Refer to the commands I showed you above.", "");      

Don't forget to remove any sensitive data. :slight_smile:

1 Like

@noobide the repo was just uptdated.

This is the direct link to the PIR example:

For the video stream I'll investigate, but as I remember, there is no methods for this in Telegram public API and anyway with such poor bandwith speed (max 100kb/s) I think is not possible stream video succesfully.

The CameraWebServer.ino example work very well because is a local LAN and speed are more than enough.

For shure, is possible store a short video in the SD memory and then send as a message.
I'll work on this in the next week.

1 Like

Hello, please I need your help on the monitor I only see the following and I accessed from Files -> Examples -> ESP32 -> Camera -> CameraWebServer.

Hi @will19742 ,

Welcome to the forum! What is your problem? Open or in a browser and you should see your camera feed.


@kgray9 Forget that problem. Now all works for me including bots
/ FlashOn… etc.

The only thing that does not work is that it does not take the photo with PIR sensor and I already tried the sensor with another circuit and it works but when requesting / PIROn nothing happens. I used the @noobide final code.

Hi @will19742, so you're saying the command /PIROn doesn't work, but does it notify you when a motion is detected though ? And does the command /photo works fine ? If yes for the first question, and no for the second, the problem might be coming from your esp.

Also you sure you're using the 5v esp pin and not 3.3v right ?!

How about you try with the Rui Santos sketch and see if the pir takes a picture

With this sketch the pir stays on all the time and you don't have to open the WebCameraServer sketch. Let us know :slight_smile:

Hello @noobide , very kind for your answer, I tell you that the commands all work including the photo, but when I send the PIRon command the serial monitor only shows "PIRon" but then it does not show anything else, I also receive the message in Telegram that the sensor it is active but the sensor does not send the signal, also I am using the 5 volts to power the PIR sensor and I even fed it with an external source (I thought my problem could be power) but it still does not work, I have tried with all the sketches of this thread including that of Rui Santos and the PIR sensor does not work in all cases, as I said before I have tested the sensor independently with up to 3 different PIR sensor and two ESP32 cam. I give up :frowning: :frowning: :frowning: