ESP32 and ESP8266 boards together with Telegram are a great combination. You can send data to your smartphone at lightning speed and control your microcontroller from there.

In this project you create a photo trap that snaps as soon as someone moves in front of the camera. Then it takes a photo and sends it immediately to your smartphone.


1 hour

ca. $20

For this project you need (quantities see description):

Before you can start, you have to spend a few minutes in preparation.

Prepare Telegram

First you need an account with Telegram – and the corresponding app for your smartphone or computer. In the following we use a Smartphone. Telegram is free, ad-free and works similar to WhatsApp. But here you have the possibility to create bots that you can interact with.

You can take advantage of that in this project by letting your ESP8266 “talk” to your Telegram bot. The bot in turn will immediately send you a notification.

Create a Telegram bot

If you haven’t already done so, download Telegram from the Play Store or App Store to your smartphone. Create an account there and then search for Botfather in the Chats section.

Open the botfather and tap Start. After his welcome message you will receive instructions. You can read them now, or tap on /newbot. After that the botfather will ask you for a name for your bot and a username:

After you have assigned the names, you will receive a message with your token. You will need this token later in your sketch to connect your ESP8266 to your telegram bot.

Get your User ID

You have now created your Telegram bot. Now you need your User ID to send messages to the bot. This also takes only a few seconds.

In the Telegram app go back to the chats and search for IDBot:

Now tap on IDBot and in the following chat window tap on /getid. You will immediately receive a message with your User ID:

And these were already the preparations in Telegram. Keep your token and your user ID ready to enter them into the sketch later. And keep them secret, so that nobody else uses your bot.

The library UniversalTelegramBot.h

With this library you control the entire communication between your ESP32-CAM and your Telegram bot.

Search the library manager for UniversalTelegramBot and install version 1.1.0

Note: If you have problems with the version from the library manager, download this version directly from us. Afterwards, embed the library in the Arduino IDE.

The other libraries in the sketch should already be installed if you have already made your ESP32-CAM available in the Arduino IDE.

Building the camera trap

Unfortunately the ESP32-CAM has no USB port, so you have to use an FTDI programmer to upload sketches. If you haven’t been able to gain any experience here, this tutorial will show you how to program the ESP32-CAM.

If you have connected the USB/Serial adapter, only the motion sensor is missing. In this project we use the PIR sensor HC-SR501 for which you only need a data pin.

Orientate yourself by this schematic:

Here again the connections are clearly displayed:

FTDI adapter






Note: In the scheme above you will find a bridge in grey – always remember that you need this to upload a sketch. Once the code is on your ESP32 CAM, disconnect the bridge and press the RESET button on the board. Only then your sketch will start.

The sketch

For this project we took a sketch of Random Nerd Tutorials as a starting point and expanded it. Rui Santos uses the ESP32-CAM to save a photo on an SD card when he moves.

We have modified the design and code so that your board sends the photo to your Telegram-Bot, i.e. to your smartphone, as soon as motion is detected.

You can find the whole sketch on Github as .txt and at the end of this article.

Important adjustments in the code of the camera trap

To make the sketch work for you, you have to adjust a few things. First enter the access data of your Wi-Fi network so that the ESP32-CAM can connect to it.

Then you need your token and the User ID you received when you created your telegram bot.

const char* ssid = "NETWORK";
const char* password = "PASSWORD";
String BOTtoken = "TOKEN"; 
String CHAT_ID = "USERID";

A feature that you can customize to your liking is the time between photos. In the loop you will find a delay() of 20 seconds. This is the period of time in which the motion sensor does not register any further movement after a photo has been sent.

The shorter you set this time span, the more photos you will get – as long as someone keeps moving in front of the camera.

You can also adjust the sensitivity of your HC-SR501. There are two potentiometers on it: If you turn the board upwards, you can adjust the sensitivity on the left of them. Experiment a bit to find the right setting for you.

And that should have been it. One more thing: This project is for testing. If you want to use the photo trap productively, think about the IT security beforehand and make sure not to violate any personal rights!

Here the whole sketch to copy out:

  Rui Santos
  Complete project details at

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.

  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.

  Adapted by Pollux Labs –

#include "soc/soc.h"
#include "soc/rtc_cntl_reg.h"
#include "esp_camera.h"

const char* ssid = "NETWORK";
const char* password = "PASSWORD";

// Initialize Telegram BOT
String BOTtoken = "TOKEN";  // your Bot Token (Get from Botfather)

// Use @myidbot to find out the chat ID of an individual or a group
// Also note that you need to click "start" on a bot before it can
// message you
String CHAT_ID = "CHAT-ID";

bool sendPhoto = false;

WiFiClientSecure clientTCP;
UniversalTelegramBot bot(BOTtoken, clientTCP);

//Pin of the motion sensor
#define PIR_PIN 13

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

  // Drop down frame size for higher initial frame rate
  sensor_t * s = esp_camera_sensor_get();

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

    uint8_t *fbBuf = fb->buf;
    size_t fbLen = fb->len;
    for (size_t n = 0; n  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 (c == '\n')
          if (getAll.length() == 0) state = true;
          getAll = "";
        else if (c != '\r')
          getAll += String(c);
        if (state == true) getBody += String(c);
        startTimer = millis();
      if (getBody.length() > 0) break;
  else {
    getBody = "Connected to failed.";
    Serial.println("Connected to failed.");
  return getBody;

void setup() {
  // Init Serial Monitor

  //Set PIR
  pinMode(PIR_PIN, INPUT);

  // Config and init the camera

  // Connect to Wi-Fi
  Serial.print("Connecting to ");
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
  Serial.print("ESP32-CAM IP Address: ");

void loop() {
  if (digitalRead(PIR_PIN)) {
    Serial.println("Preparing photo");

Letzte Aktualisierung am 2021-02-28 / Affiliate Links / Bilder von der Amazon Product Advertising API