In this project we’ll create a PCB shield for the ESP32-CAM AI-Thinker board with a PIR motion sensor, a BME280 temperature, humidity and pressure sensor and some additional exposed pins. We’ll create a Telegram bot for the ESP32-CAM that allows you to control your board from anywhere to request a photo, sensor readings or control the flash. Additionally, you’ll receive a notification with a new photo whenever motion is detected.
Alternatively, you can also follow this project by wiring the circuit on a breadboard.
Watch the Video Tutorial
This project is available in video format and in written format. You can watch the video below or you can scroll down for the written instructions.
Resources
You can find all the resources needed to build this project in the links below (or you can visit the GitHub project):
- ESP32-CAM Code (Arduino IDE)
- Gerber files
- EasyEDA project to edit the PCB
- Click here to download all the files
Project Overview
This project consists of three parts:
- Designing and Building the PCB shield
- Creating the Telegram Bot
- Programming the PCB shield using Arduino IDE
ESP32-CAM PCB Shield Features
The PCB shield is designed to be stacked to the ESP32-CAM. For this reason, if you want to use our PCB, you need the same ESP32-CAM board. We’re using the ESP32-CAM AI-Thinker Module.
We’re also using a camera module with a longer ribbon. So that when you mount the shield, the camera is on the same side of the PIR motion sensor.
Alternatively, you can also assemble the circuit on a breadboard.
The shield consists of:
- BME280 temperature, humidity and pressure sensor (4 pins);
- Mini PIR motion sensor (AM312);
- Exposed 5V and GND pins to power up the shield and ESP32-CAM;
- Other exposed GPIOs if you want to add additional features.
ESP32-CAM PCB Shield Pin Assignment
This is the pin assignment for the BME280 and PIR motion sensor on the PCB shield:
- PIR Motion Sensor: GPIO 13
- BME280: GPIO 14 (SDA), GPIO 15 (SCL)
ESP32-CAM Telegram Bot
To control the ESP32-CAM shield, we’ll create a Telegram bot, so that you can monitor your ESP32-CAM from anywhere (as long as you have internet access in your smartphone). You can use the following commands to interact with your bot:
- /start: sends a welcome message with the valid commands to control the shield;
- /flash: toggles the ESP32-CAM LED Flash;
- /photo: takes a new photo and sends it to your Telegram account;
- /readings: requests the latest BME280 sensor readings.
Additionally, you’ll receive a notification with a photo whenever motion is detected. Finally, only you (or any other authorized user that you want) can control the ESP32-CAM using Telegram.
Testing the Circuit on a Breadboard
Before designing and building the PCB shield, it’s important to test the circuit on a breadboard. If you don’t want to make a PCB, you can still follow this project by assembling the circuit on a breadboard.
Parts Required
To assemble the circuit on a breadboard you need the following parts:
- ESP32-CAM AI-Thinker
- Mini PIR motion sensor
- BME280 (4 pins)
- FTDI programmer (to upload code)
- Breadboard
- Jumper wires
You can use the preceding links or go directly to MakerAdvisor.com/tools to find all the parts for your projects at the best price!
After gathering all the parts, assemble the circuit by following the next schematic diagram:
Designing the PCB
To design the circuit and PCB, we used EasyEDA which is a browser based software to design PCBs. If you want to customize your PCB, you just need to upload the following files:
Designing the circuit works like in any other circuit software tool, you place some components and you wire them together. Then, you assign each component to a footprint.
Having the parts assigned, place each component. When you’re happy with the layout, make all the connections and route your PCB.
Save your project and export the Gerber files.
Note: you can grab the project files and edit them to customize the shield for your own needs.
Ordering the PCBs at PCBWay
This project is sponsored by PCBWay. PCBWay is a full feature Printed Circuit Board manufacturing service.
Turn your DIY breadboard circuits into professional PCBs – get 10 boards for approximately $5 + shipping (which will vary depending on your country).
Once you have your Gerber files, you can order the PCB. Follow the next steps to download the file.
1. Download the Gerber files – click here to download the .zip file
2. Go to PCBWay website and open the PCB Instant Quote page.
3. PCBWay can grab all the PCB details and automatically fill them for you. Use the “Quick-order PCB (Autofill parameters)”.
4. Press the “+ Add Gerber file” button to upload the provided Gerber files.
And that’s it. You can also use the OnlineGerberViewer to check if your PCB is looking as it should.
If you aren’t in a hurry, you can use the China Post shipping method to lower your cost significantly. In our opinion, we think they overestimate the China Post shipping time.
You can increase your PCB order quantity and change the solder mask color. I’ve ordered the Blue color.
Once you’re ready, you can order the PCBs by clicking “Save to Cart” and complete your order.
Unboxing
After approximately one week using the DHL shipping method, I received the PCBs at my office.
Everything comes well packed, and the PCBs are really high-quality. The letters on the silkscreen are really well-printed and easy to read. Additionally, the solder sticks easily to the pads.
Besides the PCBs, I also received some gifts (celebration of their 6th anniversary): a badge, some stickers, a t-shirt, a pen and some rulers.
Soldering the Components
The next step is soldering the components to the PCB. You just need to solder female header pins. The PIR motion sensor and the BME280 will then connect to those pins.
Here’s a list of all the components needed to build the PCB shield:
Here’s the soldering tools I’ve used:
Read our review about the TS80 Soldering Iron: TS80 Soldering Iron Review – Best Portable Soldering Iron.
The soldering process is pretty simple as you just need to solder the headers pins. There are some exposed GPIOs in the middle of the shield. Solder pins to those GPIOs if you want to use them to connect any other peripherals.
Here’s how the ESP32-CAM PCB Shield looks like after assembling.
Creating a Telegram Bot
The ESP32-CAM will interact with a Telegram bot to receive and handle the messages, and send responses to your Telegram account (sensor readings and photos). Follow the next steps to create a Telegram bot.
Go to Google Play or App Store, download and install Telegram.
Open Telegram and follow the next steps to create a Telegram Bot. First, search for “botfather” and click the BotFather as shown below. Or open this link t.me/botfather in your smartphone.
The following window should open and you’ll be prompted to click the start button.
Type /newbot and follow the instructions to create your bot. Give it a name and username.
If your bot is successfully created, you’ll receive a message with a link to access the bot and the bot token. Save the bot token because you’ll need it so that the ESP32/ESP8266 can interact with the bot.
Get Your Telegram User ID
Anyone that knows your bot username can interact with it. To make sure that we ignore messages that are not from our Telegram account (or any authorized users), you can get your Telegram User ID. Then, when your telegram bot receives a message, the ESP can check whether the sender ID corresponds to your User ID and handle the message or ignore it.
In your Telegram account, search for “IDBot” or open this link t.me/myidbot in your smartphone.
Start a conversation with that bot and type /getid. You will get a reply back with your user ID. Save that user ID, because you’ll need it later in this tutorial.
Preparing Arduino IDE
We’ll program the ESP32-CAM using Arduino IDE, so make sure you have the ESP32 add-on installed in your Arduino IDE.
Universal Telegram Bot Library
To interact with the Telegram bot, we’ll use the Universal Telegram Bot Library created by Brian Lough that provides an easy interface for the Telegram Bot API.
Follow the next steps to install the latest release of the library.
- Click here to download the Universal Arduino Telegram Bot library.
- Go to Sketch > Include Library > Add .ZIP Library...
- Add the library you’ve just downloaded.
And that’s it. The library is installed.
Important: don’t install the library through the Arduino Library Manager because it might install a deprecated version.
For all the details about the library, take a look at the Universal Arduino Telegram Bot Library GitHub page.
ArduinoJson Library
You also have to install the ArduinoJson library. Follow the next steps to install the library.
- Go to Sketch > Include Library > Manage Libraries.
- Search for “ArduinoJson”.
- Install the library.
We’re using ArduinoJson library version 6.15.2.
BME280 SparkFun Library
In most of our projects with the BME280 sensor, we use the Adafruit_BME280 library. However, it conflicts with some of the ESP32-CAM libraries. So, to avoid modifying the library files, we used the BME280 Sparkfun library instead that works well with the ESP32-CAM. Follow the next steps to install the BME280 Sparkfun library.
- Go to Sketch > Include Library > Manage Libraries.
- Search for “Sparkfun BME280”.
- Install the library.
Control ESP32-CAM with Telegram – Arduino Sketch
The following sketch allows you to control the ESP32-CAM using your Telegram account. You’ll also receive a notification with a photo when motion is detected.
Copy the following code to your Arduino IDE. To make it work for you, you need to insert your network credentials (SSID and password), your Telegram Bot Token and your Telegram User ID.
/*
Rui Santos
Complete project details at https://RandomNerdTutorials.com/esp32-cam-shield-pcb-telegram/
Project created using Brian Lough's Universal Telegram Bot Library: https://github.com/witnessmenow/Universal-Arduino-Telegram-Bot
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
*/
#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 "SparkFunBME280.h"
// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
// 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 chatId = "XXXXXXXXXX";
// Initialize Telegram BOT
String BOTtoken = "XXXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
bool sendPhoto = false;
WiFiClientSecure clientTCP;
UniversalTelegramBot bot(BOTtoken, clientTCP);
//CAMERA_MODEL_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
#define FLASH_LED_PIN 4
bool flashState = LOW;
// Motion Sensor
bool motionDetected = false;
// Define I2C Pins for BME280
#define I2C_SDA 14
#define I2C_SCL 15
BME280 bme;
int botRequestDelay = 1000; // mean time between scan messages
long lastTimeBotRan; // last time messages' scan has been done
void handleNewMessages(int numNewMessages);
String sendPhotoTelegram();
// Get BME280 sensor readings and return them as a String variable
String getReadings(){
float temperature, humidity;
temperature = bme.readTempC();
//temperature = bme.readTempF();
humidity = bme.readFloatHumidity();
String message = "Temperature: " + String(temperature) + " ºC \n";
message += "Humidity: " + String (humidity) + " % \n";
return message;
}
// Indicates when motion is detected
static void IRAM_ATTR detectsMovement(void * arg){
//Serial.println("MOTION DETECTED!!!");
motionDetected = true;
}
void setup(){
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
Serial.begin(115200);
pinMode(FLASH_LED_PIN, OUTPUT);
digitalWrite(FLASH_LED_PIN, flashState);
// Init BME280 sensor
Wire.begin(I2C_SDA, I2C_SCL);
bme.settings.commInterface = I2C_MODE;
bme.settings.I2CAddress = 0x76;
bme.settings.runMode = 3;
bme.settings.tStandby = 0;
bme.settings.filter = 0;
bme.settings.tempOverSample = 1;
bme.settings.pressOverSample = 1;
bme.settings.humidOverSample = 1;
bme.begin();
WiFi.mode(WIFI_STA);
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
clientTCP.setCACert(TELEGRAM_CERTIFICATE_ROOT); // Add root certificate for api.telegram.org
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(500);
}
Serial.println();
Serial.print("ESP32-CAM IP Address: ");
Serial.println(WiFi.localIP());
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_sccb_sda = SIOD_GPIO_NUM;
config.pin_sccb_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);
delay(1000);
ESP.restart();
}
// Drop down frame size for higher initial frame rate
sensor_t * s = esp_camera_sensor_get();
s->set_framesize(s, FRAMESIZE_CIF); // UXGA|SXGA|XGA|SVGA|VGA|CIF|QVGA|HQVGA|QQVGA
// PIR Motion Sensor mode INPUT_PULLUP
//err = gpio_install_isr_service(0);
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);
}
}
void loop(){
if (sendPhoto){
Serial.println("Preparing photo");
sendPhotoTelegram();
sendPhoto = false;
}
if(motionDetected){
bot.sendMessage(chatId, "Motion detected!!", "");
Serial.println("Motion Detected");
sendPhotoTelegram();
motionDetected = false;
}
if (millis() > lastTimeBotRan + botRequestDelay){
int numNewMessages = bot.getUpdates(bot.last_message_received + 1);
while (numNewMessages){
Serial.println("got response");
handleNewMessages(numNewMessages);
numNewMessages = bot.getUpdates(bot.last_message_received + 1);
}
lastTimeBotRan = millis();
}
}
String sendPhotoTelegram(){
const char* myDomain = "api.telegram.org";
String getAll = "";
String getBody = "";
camera_fb_t * fb = NULL;
fb = esp_camera_fb_get();
if(!fb) {
Serial.println("Camera capture failed");
delay(1000);
ESP.restart();
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;
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");
clientTCP.println();
clientTCP.print(head);
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);
}
}
clientTCP.print(tail);
esp_camera_fb_return(fb);
int waitTime = 10000; // timeout 10 seconds
long startTimer = millis();
boolean state = false;
while ((startTimer + waitTime) > millis()){
Serial.print(".");
delay(100);
while (clientTCP.available()) {
char c = clientTCP.read();
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;
}
clientTCP.stop();
Serial.println(getBody);
}
else {
getBody="Connected to api.telegram.org failed.";
Serial.println("Connected to api.telegram.org failed.");
}
return getBody;
}
void handleNewMessages(int numNewMessages){
Serial.print("Handle New Messages: ");
Serial.println(numNewMessages);
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", "");
continue;
}
// Print the received message
String text = bot.messages[i].text;
Serial.println(text);
String fromName = bot.messages[i].from_name;
if (text == "/flash") {
flashState = !flashState;
digitalWrite(FLASH_LED_PIN, flashState);
}
if (text == "/photo") {
sendPhoto = true;
Serial.println("New photo request");
}
if (text == "/readings"){
String readings = getReadings();
bot.sendMessage(chatId, readings, "");
}
if (text == "/start"){
String welcome = "Welcome to the ESP32-CAM Telegram bot.\n";
welcome += "/photo : takes a new photo\n";
welcome += "/flash : toggle flash LED\n";
welcome += "/readings : request sensor readings\n\n";
welcome += "You'll receive a photo whenever motion is detected.\n";
bot.sendMessage(chatId, welcome, "Markdown");
}
}
}
How the Code Works
Continue reading to learn how the code works, or skip to the next section.
Importing Libraries
Start by importing the required libraries.
#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 "SparkFunBME280.h"
Network Credentials
Insert your network credentials in the following variables.
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";
Telegram User ID
Insert your Telegram chat ID on the chatId variable. The one you’ve got from the IDBot.
String chatId = "XXXXXXXXXX";
Telegram Bot Token
Insert your Telegram Bot token you’ve got from Botfather on the BOTtoken variable.
String BOTtoken = "XXXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX";
The sendPhoto Boolean variable indicates whether it is time to send a new photo to your telegram account. By default, it is set to false.
bool sendPhoto = false;
Create a new WiFi client with WiFiClientSecure.
WiFiClientSecure clientTCP;
Create a bot with the token and client defined earlier.
UniversalTelegramBot bot(BOTtoken, clientTCP);
Camera Pins
Define the pins used by the ESP32-CAM:
//CAMERA_MODEL_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
This is the pin definition for the AI-Thinker board, if you’re using another camera model, check the pinout for your board: ESP32-CAM Camera Boards: Pin and GPIOs Assignment Guide.
Flash LED
Create a variable to hold the flash LED pin (FLASH_LED_PIN). In the ESP32-CAM AI‑Thinker, the flash is connected to GPIO 4. By default, set it to LOW.
#define FLASH_LED_PIN 4
bool flashState = LOW;
Motion Sensor
The motionDetected variable indicates whether motion has been detected. It is set to false by default.
bool motionDetected = false;
BME280
Define the SDA and SCL pins to be used with the BME280.
#define I2C_SDA 14
#define I2C_SCL 15
Create a BME280 instance called bme.
BME280 bme;
Request Delay
The botRequestDelay and lasTimeBotRan variables are used to check for new Telegram messages every x number of seconds. In this case, the code will check for new messages every second (1000 milliseconds). You can change that delay time in the botRequestDelay variable.
int botRequestDelay = 1000; // mean time between scan messages
long lastTimeBotRan; // last time messages' scan has been done
handleNewMessages()
The handleNewMessages() function handles what happens when new messages arrive.
void handleNewMessages(int numNewMessages){
Serial.print("Handle New Messages: ");
Serial.println(numNewMessages);
Get the chat ID for that particular message and store it in the chat_id variable. The chat ID identifies who sent the message.
String chat_id = String(bot.messages[i].chat_id);
If the chat_id is different from your chat ID (chatId), it means that someone (that is not you) has sent a message to your bot. If that’s the case, ignore the message and wait for the next message.
if (chat_id != chatId){
bot.sendMessage(chat_id, "Unauthorized user", "");
continue;
}
Otherwise, it means that the message was sent from a valid user, so we’ll save it in the text variable and check its content.
String text = bot.messages[i].text;
Serial.println(text);
The from_name variable saves the name of the sender.
String fromName = bot.messages[i].from_name;
If it receives the /flash message, invert the flashState variable and update the flash led state. If it was previously LOW, set it to HIGH. If it was previously HIGH, set it to LOW.
if (text == "/flash") {
flashState = !flashState;
digitalWrite(FLASH_LED_PIN, flashState);
}
If it receives the /photo message, set the sendPhoto variable to true. Then, in the loop(), we’ll check the value of the sendPhoto variable and proceed accordingly.
if (text == "/photo") {
sendPhoto = true;
Serial.println("New photo request");
}
If it receives the /readings message, call the getReadings() function (we’ll take a look at that function later on) and send the readings to the bot.
if (text == "/readings"){
String readings = getReadings();
bot.sendMessage(chatId, readings, "");
}
Sending a message to the bot is very simple. You just need to use the sendMessage() method on the bot object and pass as arguments the recipient’s chat ID, the message, and the parse mode.
bool sendMessage(String chat_id, String text, String parse_mode = "")
Finally, if it receives the /start message, we’ll send the valid commands to control the ESP. This is useful if you happen to forget what are the commands to control your board.
if (text == "/start"){
String welcome = "Welcome to the ESP32-CAM Telegram bot.\n";
welcome += "/photo : takes a new photo\n";
welcome += "/flash : toggle flash LED\n";
welcome += "/readings : request sensor readings\n\n";
welcome += "You'll receive a photo whenever motion is detected.\n";
bot.sendMessage(chatId, welcome, "Markdown");
}
sendPhotoTelegram()
The sendPhotoTelegram() function takes a photo with the ESP32-CAM.
camera_fb_t * fb = NULL;
fb = esp_camera_fb_get();
if(!fb) {
Serial.println("Camera capture failed");
delay(1000);
ESP.restart();
return "Camera capture failed";
}
Then, it makes an HTTP POST request to send the photo to your telegram bot.
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");
clientTCP.println();
clientTCP.print(head);
getReadings()
The getReadings() function requests temperature and humidity from the BME280 sensor.
String getReadings(){
float temperature, humidity;
temperature = bme.readTempC();
//temperature = bme.readTempF();
humidity = bme.readFloatHumidity();
The readings are concatenated in the message variable that is returned by the function.
String message = "Temperature: " + String(temperature) + " ºC \n";
message += "Humidity: " + String (humidity) + " % \n";
return message;
detectsMovement()
The detectsMovement() is a callback function that is called when motion is detected. In this case, we set the motionDetected variable to true. Then, in the loop(), we’ll handle what happens when there’s motion (sends a photo).
static void IRAM_ATTR detectsMovement(void * arg){
//Serial.println("MOTION DETECTED!!!");
motionDetected = true;
}
setup()
In the setup(), initialize the Serial Monitor.
Serial.begin(115200);
Set the flash LED as an output and set it to its initial state.
pinMode(FLASH_LED_PIN, OUTPUT);
digitalWrite(FLASH_LED_PIN, flashState);
Initialize the BME280 sensor:
// Init BME280 sensor
Wire.begin(I2C_SDA, I2C_SCL);
bme.settings.commInterface = I2C_MODE;
bme.settings.I2CAddress = 0x76;
bme.settings.runMode = 3;
bme.settings.tStandby = 0;
bme.settings.filter = 0;
bme.settings.tempOverSample = 1;
bme.settings.pressOverSample = 1;
bme.settings.humidOverSample = 1;
bme.begin();
Connect your ESP32-CAM to your local network.
WiFi.mode(WIFI_STA);
Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(500);
}
Serial.println();
Serial.print("ESP32-CAM IP Address: ");
Serial.println(WiFi.localIP());
Configure and initialize the camera.
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);
delay(1000);
ESP.restart();
}
// Drop down frame size for higher initial frame rate
sensor_t * s = esp_camera_sensor_get();
s->set_framesize(s, FRAMESIZE_CIF); // UXGA|SXGA|XGA|SVGA|VGA|CIF|QVGA|HQVGA|QQVGA
Setup an interrupt on GPIO 13:
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);
}
loop()
In the loop(), check the state of the sendPhoto variable. If it is true, call the sendPhotoTelegram() function to take and send a photo to your telegram account.
if (sendPhoto){
Serial.println("Preparing photo");
sendPhotoTelegram();
sendPhoto = false;
}
When it’s done, set the sendPhoto variable to false.
sendPhoto = false;
When motion is detected, send a notification to your Telegram account and call the senPhototoTelegram() function. Then, set the motionDetected variable to false.
if(motionDetected){
bot.sendMessage(chatId, "Motion detected!!", "");
Serial.println("Motion Detected");
sendPhotoTelegram();
motionDetected = false;
}
Check for new Telegram messages every second.
if (millis() > lastTimeBotRan + botRequestDelay){
int numNewMessages = bot.getUpdates(bot.last_message_received + 1);
while (numNewMessages){
Serial.println("got response");
handleNewMessages(numNewMessages);
numNewMessages = bot.getUpdates(bot.last_message_received + 1);
}
lastTimeBotRan = millis();
}
When a new message arrives, call the handleNewMessages() function.
while (numNewMessages){
Serial.println("got response");
handleNewMessages(numNewMessages);
numNewMessages = bot.getUpdates(bot.last_message_received + 1);
}
That’s pretty much how the code works.
Upload Code to the ESP32-CAM
After making the necessary changes, upload the code to your ESP32-CAM (before connecting the shield). Follow the next steps to upload code or follow this tutorial: How to upload code to ESP32-CAM.
1) Wire the ESP32-CAM to the FTDI programmer as shown in the following diagram.
Note: the order of the FTDI pins on the diagram may not match yours. Make sure you check the silkscreen label next to each pin.
Important: GPIO 0 needs to be connected to GND so that you’re able to upload code.
2) Go to Tools > Board and select AI-Thinker ESP32-CAM. You must have the ESP32 add-on installed. Otherwise, this board won’t show up on the Boards menu.
3) Go to Tools > Port and select the COM port the ESP32-CAM is connected to.
4) Then, click the Upload button in your Arduino IDE.
5) When you start to see some dots on the debugging window, press the ESP32-CAM on-board RST button.
After a few seconds, the code should be successfully uploaded to your board.
6) When you see the “Done uploading” message, remove GPIO 0 from GND.
Open the Serial Monitor, press the on-board RST button, and check that the ESP32-CAM is connecting to your network without any problems.
Demonstration
With the code uploaded to your ESP32-CAM, attach the PCB shield and all the components.
Apply power using the 5V and GND pins on the shield.
Then, press the ESP32-CAM RST button, so that it starts running the code.
Now, open your Telegram account and test your board. Send the following messages to your ESP32 Telegram bot to control your ESP32-CAM:
- /start: sends a welcome message with the valid commands to control the shield;
- /flash: toggles the ESP32-CAM LED Flash;
- /photo: takes a new photo and sends it to your Telegram account;
- /readings: requests the latest BME280 sensor readings.
Additionally, you’ll receive a notification with a photo whenever motion is detected.
If you try to interact with your bot from another account, you’ll get the the “Unauthorized user” message.
Wrapping Up
In this tutorial we’ve created a PCB shield for the ESP32-CAM with a PIR motion sensor and BME280. This creates a more permanent circuit in a small footprint that you can put inside a small enclosure or dummy camera.
You also learned how to use your Telegram account to control your ESP32-CAM using a Telegram bot. This allows you to control and monitor your board from anywhere, as long as you have internet access in your smartphone.
You can also create your own code to do any other tasks with the shield.
We have other similar projects that include building and designing PCBs that you may like:
- ESP32 IoT Shield PCB with Dashboard for Outputs and Sensors
- Build an All-in-One ESP32 Weather Station Shield
- Build a Multisensor Shield for ESP8266
- EXTREME POWER SAVING with Microcontroller External Wake Up: Latching Power PCB
Learn more about the ESP32-CAM with our resources:
We’re giving away 5 bare PCBs to someone that posts a comment below (comments might take up to 24 hours to be approved)! Simply post a comment in this blog post about what you would like to do with the PCB and you’re entered for a chance to win one of these bare PCBs. We’re currently confirm the winners and we will announce them during this weekend (August 22). So, stay tuned! [Update] the giveaway ended and the winners are: Gerald Maurer, Jason Wilkins, Svein Utne, Domenico Carvetta, and João Paulo.
This tutorial offers a clear insight in the whole process off processing and forwarding data. So I sure will to analyse also this example. One thing that thrills me with this example: how can I adapt the software, so that it can run on batteries. Maybe by using deepsleep and build some low current circuit that awakes the software of the ESP32. Maybe by using an attiny85 that itself wakes up every 10 seconds and then very shortly tests movement.
Very appreciated your project. This will be one more opportunity I have to carry out for the next weeks.
Thanks a lot.
Really appreciate your hard work. I will be really happy if I will get the PCB that you are going to offer.
Great looking project, if I had the PCB I would modify it to control a servo. As is, the code could be altered to include a deep sleep routine allowing extended battery life. I’m going to have a go at making this and modifying the software myself!
Dear Sara,
why don’t use directly the instruction: uint32_t instead of uint16_t to cover the UXGA format? Am I wrong ?
Thanks. Domenico
Molto interessante come progetto. Mi piacerebbe usarlo per assemblare, anche con l’aiuto di una stampante 3d, la mia prima ipCam completamente programmabile. Mi piacerebbe anche poter sfruttare i restanti PIN rimasti liberi per poter eccitare eventualmente dei relay all’occorrenza. Grazie a voi per questo tutorial.
Very interesting as a project. I would like to use it to assemble, even with the help of a 3d printer, my first fully programmable ipCam. I would also like to be able to take advantage of the remaining PINs left free to eventually excite relays if necessary. Thanks to you for this tutorial.
I really appreciate the whole process including PCB. Been researching this as a part of other projects. So glad to see this included here! Kudos to you.
There are a few things wrong with this sketch.
First off it’s missing the declaration of the clientTCP variable (WiFiClientSecure clientTCP;).
Then there are these extraneous chatId variables (I replaced them with CHAT_ID).
Finally you need to add String casts around CHAT_ID and BOTtoken wherever they are used.
Once all that is done it compiles and works.
And to Domenico’s point, for low res camera captures 16bit is OK but I changed the code to 32 bit so I can set my camera to a higher resolution.
Hello Steve, thank you so much for testing our code and letting me know. While removing the crendetials, we’ve copied the wrong bot token and chat id definitions. We’ve also deleted the WiFiClientSecure declaration by mistake (while explaining how the code works).
All those changes have been made and the code now compiles.
Thanks again!
Works like the proverbial dream.
Thank you for getting it right in the end.
while trying to test it my esp 32 keep connecting and disconnecting to the network
why is that so?
Thanks for this excellent tutorial (as all your tutorials, very clear and interesting).
I would very much like to have this custom made PCB Shield to try an adaption of this wonderful project as a remote ringbell image notification (i’ll leave the suggestion here).
The idea would be to use a dummy camera like in your suggestion, but instead of having a motion sensor, it would have an input from the main doorbell button, and an output for the conventional doorbell system, while allowing the Telegram Bot to transmit the photo of the person @ the door, taken the moment it presses the doorbell button 🙂
What do you think about that? (maybe an idea for your next tutorial?)
Hey, this is so great, I am going to try that. But would it be possible to try the very same thing on cloudmqtt? Especially the camera picture to be posted over cloudmqtt?
Thanks in advance 🙂
Translation courtesy of Google
I have read several times correctly interpret the indications that this web https://arduinojson.org/v6/doc/upgrade/
but I have to admit that, after several failed attempts,
The solution of updating Brian Lough’s UniversalTelegramBot.cpp library version 1.1.0 (installed) to adapt it to version 6.x of ArduinoJson is beyond my possibilities. Sorry
I would appreciate a copy of the UniversalTelegramBot.cpp file from whoever obtained it, or the indications of the commands that I must modify.
Translation courtesy of Google
I got it !!, after much struggle, I have managed to get UniversalTelegramBot to understand with ArduinoJson V6.16.1. (Installed)
I no longer have compilation errors. Thank you all !!
Dear Sara, about the PIR Motion Sensor mode INPUT_PULLUP, I found that sometime you use as follows:
err = gpio_isr_handler_add(GPIO_NUM_13, &detectsMovement, (void *) 13);
sometime also:
esp_err_t err = gpio_isr_handler_add(GPIO_NUM_13, &detectsMovement, (void *) 13);
I guess both works fine, but unknown the difference, thanks for letting us know.
Very good project.
I have been waiting for something like this for several years, but now I got it.
Today I put one ESP32-Cam in my sailboat, with an old phone that is providing the internet connection with an extra data sim card. This cost less than €3 per month. Now I plan to also put one or two in my cottage. I might want to use two at home two in the sailboat and two at the cottage.
Then the question is: what do I need to change? The chatId or the BOTtoken or both?
Not necessary. You can use same Bot name and bot_token for all stuff.
Nice, I will try that tomorrow.
Yes, just be sure to change the text on the “bot.sendMessage” line of code on each ESP32-CAM so you can identify which sensor is sending the message!!!
So, instead of “Motion detected!!”, you should put “Home entry Motion detected!!”, “Sailboat entry Motion detected!!” or whatever makes you identify the origin of motion 🙂
Thanks a lot for this help. What if I want to send a message to one of the ESP32-CAM, then maybe I need different chatId for each?
No, chatID is unique. It dipends on how many “botname” you want to use: one for all devices, or one for each.
Great project. I put it together and got it running with minimal effort. Your instructions were excellent.
I added two commands; moff and mon – which disable and enable the checking of the motion sensor. I did this by adding a variable;
bool motionDetectEnable = false;
and modifying the check of motionDetected as follows;
if(motionDetected && motionDetectEnable) {
And of course adding code to process the commands;
if (text == “/mon”) {
motionDetectEnable = true;
Serial.println(“New motion detect ON request”);
}
if (text == “/moff”) {
motionDetectEnable = false;
Serial.println(“New motion detect OFF request”);
}
I did this because I seem to get an abundance of motionDetected even when in a quiet room.
I’ve also noticed that it often takes minutes to receive the commands. Have others encountered this?
Thanks again for your excellent instructions!
Thanks for sharing!
thanks for sharing. very useful !
Hola te saludo desde Colombia, buscando en Internet sobre IOT te encontré, es asombroso el trabajo que haces, te felicito y agradezco en nombre propio y de la comunidad el tiempo que debes dedicarle a esto. Muchas gracias y fuerza para seguir en la labor.
Dear Rui & Sara,
thanks for another great project. I’m signed up to your RNTLAB for a few years now to support the work you do.
I would like to move from using breadboards to PCB’s like you have in this one.
I started using KiCad to draw my circuits but am struggling to then create the PCB.
I have just loaded EasyEDA and it seems much more user friendly.
One problem I am struggling with is that i want to use a different ESP kit
I want to use ESP_Devkitc_v4 when i try and find that device for the footprint and to use in the circuit i cant find it. I also cannot find the one you have actually used, if i search for ESP32_DOIT_DEVKIT_V1_36_SHIELD that also is not listed.
I am obliviously missing something, would you be so kind as to advise how you would select a device that matches the device you are using in the circuit and also how you select for the correct footprint on the PCB layout.
Greatly appreciate your assistance
Jase
Hello Jason, Try to just search “ESP32”, then look for all the components and try to find the right footprint. If you type the exact board name in the search bar, you might not find it… I hope that helps!
Many thanks,
When I first searched for ESP32 after install only found about three. The next day after giving up I rebooted and then the same search term “ESP32” shows loads up and mine was listed.
Really strange
Many thanks
I run into an other problem. The HC-SR501 got a potmeter for adjusting the sensetivity, but from max to min there is very small change, so when the vind is blowing and the boat is moving, it starts to take pictures all the time even on the lowest sensetivity, so after about 20 pictures Telegram stop working for 3 hours.
For a boat a movement sensor is probably not the best option. What are you trying to detect? If it’s someone on board you may want to use a pressure mat instead.
I do not have any pressure mat, but I got some RLWL-0516. When you put in a 1M ohm resistor its sensitivity changes from 9 meters till only 4 meters. I will try that tomorrow.
It looks like the RLWL-0516 will work, when I put it in a can, and let the opening point in the direction I want to take the picture when motion. The RLWL-0516 uses radar tecnology, and it works 360 degrees even true table tops and the hull of the boat, but with a metal can, it can be stoped. The problem now is that I took too many pictures during this testing, so now I am banned again from Telegram for some hours, so I cannot make the final test before tomorrow. What is the rule Telegram is following when banning somone?
hello, I tried to load the program but after compilation the esp can’t connect to the home network. I am sure I have entered the correct password and ssid because I have copied and pasted them from another program that work. can you tell me how to solve this problem?
Hi.
When you say that it can’t connect, what happens?
Can you add more information?
Regards,
Sara
Mine connect to network and keep disconnecting, can I just take picture with it if I don’t have the bme 280 sensor yet. But I want to fix the sensor on it later
I am having the same problem, it gets as far as showing IP address but then stops. If I run a basic sketch without the telgram side of things it connects to home server.?
Today I received some AM312 PIR. Thay are small and has only 3 meter range, but that was just what I needed, so now it looks to be working fine. We will know more in a day or two.
Useful project. Thanks for all the work you put into it. I built it and it works, it works too well, in that it constantly triggers the motion detector and sends me pictures…
Any suggestions as to why it is so sensitive and what to do about it?
Thanks,
Jacob
Try using the AM312, og is lese sensitiv. Ny problem now is that Telegram stop working after some hours, and I have to test the ESP32 to make it tun again.
Thanks for the project, it’s great, I’ve already ordered some boards.
2 questions about compatibility:
1.- use the RCWL-0516 presence sensor to replace the AM312 (it works inside the fake camera)
2.- use the DHT11 sensor to replace the BME280. (I have several )
It’s just out of curiosity
Hi Rui and Sara, sadly my comment from yesterday disappeard. Was it deleted? I tried the project but unfortunatly the bot.getUpdates() always returns 0 even though there are new messages for the bot in the chat.
I am sure the telegram bot integration works bebause I can post messages via bot.sendMessage(). So the Bot API-key, the chat_ID and my WiFi credentials are crearly okay.
Do you have any idea how to solve this issue? The issue looks similar to this one: https://github.com/witnessmenow/Universal-Arduino-Telegram-Bot/issues/121. However on ESP32 there is no more WifiClientSecure.setInsecure() method as suggested there.
Any help is much appreciated.
Hi.
I can’t find any previous comment from you.
The Telegram examples always worked fine for us with both the ESP32 and ESP8266 without any issues.
The link you’ve sent me is related with the ESP8266.
I don’t know how to solve the issue for the ESP32.
I see that you’ve opened an issue on their forum.
If you find out something, please share. Other people might be having the same issue.
Regards,
Sara
Thank you guys very much for this project. It is very nicely presented and documented. I built it and it works well first time. Now, I added a small change whereby I can enable/disable the motion detector via an additional command –
/trigg_en. That works fine however for some reason the /start command stopped working. I expected to see the “menu” every time I hit /start, but it stopped after the first time. Any ideas? I then noticed that removing the “Markdown” from bot.sendMessage(chatId, welcome, “Markdown”); fixed the problem. Any ideas?
Thanks for the interesting project. It sounds like just what I need to be able to check my mail box for delivery.
Hey. Do I have to make my own ibot for each camera? or two respond to different calls for example / photo for cam 1 and / photo wp for second cam.
Another problem is that both cam’s do not always start up when turning on, then I have to reset via the button too often. Often the cam cannot be called up, even at a short distance, and only works again after a reset or on / off of the power supply.
How much power / amp the cam actually uses. That is not clear to me.
Nice project that I enjoy working on.
Hello great tutorial
i just have one little problem
I have a pressure mat instead of the presence sensor. I connected this with a 10k PULLUP resistor.
My problem is that I keep getting motion detections.
am I doing something wrong?
best regards
Gerhard
Quote “If you try to interact with your bot from another account, you’ll get the the “Unauthorized user” message.”
I do not want this feature, since i want all of the people living in a house to receive Photo
how can i remove this authorization? or add multiple accounts to ‘authorized’ list?
Hi.
You can use a group for that, where several people inside that group can control and receive messages. We’ll post an article about that soon.
Alternatively, you can delete the following lines in the code:
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”, “”);
continue;
}
Or you can add all authorized chat IDs and check inside that if statement if it corresponds to one of the authorized IDs.
I hope this helps.
Regards,
Sara
Hey Sara,
Yes i was able to figure it out,
I tried using Group but it was difficult to get a chat_id for a group, the ID bot doesnt tell me, i was unable to get the chat id of a group.
so i just addid another statement chat_id = chatId ; and itll assign the unknown ID to current id
Great!
We’ll be publishing a tutorial about using groups tomorrow.
So, stay tuned.
Regards,
Sara
Dear Rui,
thank you for this nice project. Could you or your community send me a PCB for low price?
Hello
Please launch this project using GPRS modules.
This is great in cases where WIFI is not available.
Thanks
Thanks for your suggestion.
We’ll take a look into that.
Regards,
Sara
I am very interested in that command, I could put the code or what has been added to the original code, Thanks
Hey. Thank you for the article. Putting together the project – everything works fine. I don’t understand only one thing – why the camera settings are made
config.frame_size = FRAMESIZE_UXGA;
or
config.frame_size = FRAMESIZE_SVGA;
and the photos come from the bot with a resolution of only
FRAMESIZE_CIF, // 400×296
How can I fix this?
Sorry for my English. This is google translation ..
Hallo Sara, Rui,
Please help me out . . .
What address should I use to access my ESP32-Cam ?
When I enter /start nothing happens !
Am I missing something ?
Regards,
Met vriendelijke groeten,
Tom
Hi.
What do you mean?
With this project, you control the ESP32-CAM from your Telegram account. It is not accessible from a browser.
Regards,
Sara
Hi!
Thank you for the great content!
I followed the schematics on a breadboard and the first thing I tried to do was to run the i2c scanner in order to find the address for BME sensor but I keep getting “No I2C devices found”. I was expecting the actual address.
Am I missing something? Is it normal? Are GPIO14 (SDA) and GPIO15(SCL) the actual I2C pins? I tried with different I2C devices but got the same result.
I’ve got pictures from my setup, but can’t upload them =)
Thanks in advance!
João
Hi.
Those are not the default ESP32 I2C pins, but we set them on the code.
If you’re running an I2C scanner sketch without specifying those pins, it won’t find any sensor, because it will look up on the default pins.
Usually, the I2C address of the BME280 sensor is 0x76.
I hope this makes sense.
Regards,
Sara
Thank you very much, Sara.
It makes perfect sense.
This sentence explains everything: “Those are not the default ESP32 I2C pins, but we set them on the code.”
I will give it a try!
Thank you for the great support!
João
Great! 😀
Hello, I have this error in the compilation, so I understand the library in the IDE is missing. But I didn’t find it on the internet. Could you guide me?
Arduino: 1.8.12 (Windows 7), Placa:”AI Thinker ESP32-CAM”
Foram encontradas múltiplas bibliotecas para “WiFi.h”
esp32-cam-shield-telegram:13:23: fatal error: soc / soc.h: No such file or directory
Usado: C:\Users\Thalis Mazzarino\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.4\libraries\WiFi
compilation terminated.
Não usado: C:\Program Files (x86)\Arduino\libraries\WiFi
exit status 1
soc / soc.h: No such file or directory
Got it working after figuring out chatID is User ID.
Changed Temperature to display ºF.
Added:
// Print the Signal Strength:
long rssi = WiFi.RSSI() + 100;
Serial.print(“Signal Strength = ” + String(rssi));
if (rssi > 50) Serial.println(F(” (>50 – Good)”)); else Serial.println(F(” (Could be Better)”));
and toggled The “Flash” so it only is on when taking a picture.
if (sendPhoto) {
if (flashState == true) digitalWrite(FLASH_LED_PIN, HIGH); // FLASH ON
Serial.println(“Preparing photo”);
sendPhotoTelegram();
sendPhoto = false;
digitalWrite(FLASH_LED_PIN, LOW); // FLASH OFF
}
Removed “/” in “command” mode, for ease of use.
if (text == “Flash”) {
flashState = !flashState;
// digitalWrite(FLASH_LED_PIN, flashState);
}
if (text == “Photo”) {
sendPhoto = true;
Serial.println(“New photo request”);
}
if (text == “Readings”) {
String readings = getReadings();
bot.sendMessage(chatId, readings, “”);
}
if (text == “Start”) {
String welcome = “Welcome to the ESP32-CAM Telegram bot.\n”;
welcome += “Photo : takes a new photo\n”;
welcome += “Flash : toggle flash LED\n”;
if (flashState == true) welcome += “Flash : ON now\n”; else welcome += “Flash : OFF now\n”;
welcome += “Readings : request sensor readings\n\n”;
welcome += “You’ll receive a photo whenever motion is detected.\n”;
bot.sendMessage(chatId, welcome, “Markdown”);
}
Hi John can i get the whole code
Here is code minus my network credentials and above:
Hope this helps you.
const char* ServerName = “esp32cam”; // Address to the server with http://esp32cam.local/
String local_hwaddr; // WiFi local hardware Address
String local_swaddr; // WiFi local software Address
// Initialize Telegram BOT
// My_ESP_camBot
String chatId = “XXXXXXXX”; // User ID
String BOTtoken = “1489139834:XXXXXXXXXXXXXXXXXXXXXXXXXXX”;
bool sendPhoto = false;
WiFiClientSecure clientTCP;
UniversalTelegramBot bot(BOTtoken, clientTCP);
//CAMERA_MODEL_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
#define SENSOR_LED 12 // SENSOR_LED PIN: GPIO 12
#define FLASH_LED_PIN 4 // FLASH_LED PIN: GPIO 4
// Motion Sensor AM312 PIR sensor or XYC-WB-DC radar sensor
#define MOTION_SENSOR 13 // MOTION_SENSOR PIN: GPIO 13
bool FlashState = false;
bool MotionDetected = false;
bool MotionState = false;
// Define I2C Pins for BME280
#define I2C_SDA 14
#define I2C_SCL 15
// Create a BME280 instance called bme
BME280 bme;
int botRequestDelay = 1000; // mean time between scan messages
long lastTimeBotRan; // last time messages’ scan has been done
void handleNewMessages(int numNewMessages);
String sendPhotoTelegram();
float temperatureC;
float temperatureF;
float humidity;
// Get BME280 sensor readings and return them as a String variable
String getReadings() {
float temperatureC, temperatureF, humidity;
temperatureC = bme.readTempC();
temperatureF = bme.readTempF();
humidity = bme.readFloatHumidity();
//String message = “Temperature: ” + String(temperatureC) + ” ºC\n”;
String message = “Temperature: ” + String(temperatureF) + ” ºF\n”;
message += “Humidity: ” + String (humidity) + ” % \n”;
long rssi = WiFi.RSSI() + 100;
message += “Signal Strength: ” + String(rssi) + “\n”;
return message;
}
/*
// Indicates when motion is detected
static void IRAM_ATTR detectsMovement(void * arg) {
MotionDetected = true;
}
*/
void setup() {
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
Serial.begin(115200);
delay(500);
pinMode(MOTION_SENSOR, INPUT); // MOTION_SENSOR as INPUT
pinMode(SENSOR_LED, OUTPUT); // SENSOR_LED as OUTPUT
digitalWrite(SENSOR_LED, LOW); // Turn SENSOR LED Off
pinMode(FLASH_LED_PIN, OUTPUT); // LED as FLASH
digitalWrite(FLASH_LED_PIN, LOW); // Turn FLASH LED Off
// Init BME280 sensor
Wire.begin(I2C_SDA, I2C_SCL);
bme.settings.commInterface = I2C_MODE;
bme.settings.I2CAddress = 0x76;
bme.settings.runMode = 3;
bme.settings.tStandby = 0;
bme.settings.filter = 0;
bme.settings.tempOverSample = 1;
bme.settings.pressOverSample = 1;
bme.settings.humidOverSample = 1;
bme.begin();
delay(500);
Serial.println(“\nESP Cam using Telegram Bot”);
String readings = getReadings();
Serial.print(readings);
WiFi.mode(WIFI_STA);
Serial.println();
Serial.print(“Connecting to “);
Serial.println(ssid);
WiFi.begin(ssid, password);
// ADDED This Update
clientTCP.setCACert(TELEGRAM_CERTIFICATE_ROOT); // Add root certificate for api.telegram.org
while (WiFi.status() != WL_CONNECTED) {
Serial.print(“.”);
delay(500);
}
Serial.println(” >> CONNECTED”);
Serial.print(“ESP32-CAM IP Address: “);
Serial.println(WiFi.localIP());
// Print the Signal Strength:
long rssi = WiFi.RSSI() + 100;
Serial.print(“Signal Strength = ” + String(rssi));
if (rssi > 50) {
Serial.println(F(” (>50 – Good)”));
} else {
Serial.println(F(” (Could be Better)”));
}
/*
wifiMulti.addAP(ssid, password);
if (MDNS.begin(ServerName)) { // The name that will identify your device on the network
local_hwaddr = “http://” + WiFi.localIP().toString();
Serial.println(“Enter This Url Address \t: ” + local_hwaddr);
local_swaddr = “http://” + String(ServerName) + “.local/”;
Serial.println(” Or This Url Address \t: ” + local_swaddr);
}
else {
Serial.println(F(“ERROR setting up MDNS responder”));
}
*/
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);
delay(1000);
ESP.restart();
}
Serial.printf(“Camera Initialized >> OK \r\n”);
// Drop down frame size for higher initial frame rate
sensor_t * s = esp_camera_sensor_get();
s->set_framesize(s, FRAMESIZE_CIF); // UXGA|SXGA|XGA|SVGA|VGA|CIF|QVGA|HQVGA|QQVGA
/*
// PIR Motion Sensor mode INPUT_PULLUP
//err = gpio_install_isr_service(0);
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.printf(“Type ‘Start’ in Telegram to start bot\r\n”);
}
void loop() {
if (sendPhoto) {
Serial.println("Preparing photo");
sendPhotoTelegram();
sendPhoto = false;
}
// Read Motion Sensor
MotionDetected = digitalRead(MOTION_SENSOR);
if (MotionDetected) {
digitalWrite(SENSOR_LED, HIGH); // Turn SENSOR LED On
bot.sendMessage(chatId, “Motion detected”, “”);
Serial.println(“Motion Detected”);
digitalWrite(SENSOR_LED, LOW); // Turn SENSOR LED Off
if (MotionState) {
sendPhotoTelegram();
MotionDetected = false;
} else {
delay(30000); // Delay Wait for SENSOR to reset For Stability
}
}
if (millis() > lastTimeBotRan + botRequestDelay) {
int numNewMessages = bot.getUpdates(bot.last_message_received + 1);
while (numNewMessages) {
Serial.print(“Message received : “);
handleNewMessages(numNewMessages);
numNewMessages = bot.getUpdates(bot.last_message_received + 1);
}
lastTimeBotRan = millis();
}
}
String sendPhotoTelegram() {
const char* myDomain = “api.telegram.org”;
String getAll = “”;
String getBody = “”;
camera_fb_t * fb = NULL;
if (FlashState == true) digitalWrite(FLASH_LED_PIN, HIGH); // FLASH ON
delay(10);
fb = esp_camera_fb_get();
digitalWrite(FLASH_LED_PIN, LOW); // FLASH OFF
if (!fb) {
Serial.println(“Camera capture failed”);
delay(1000);
ESP.restart();
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;
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");
clientTCP.println();
clientTCP.print(head);
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);
}
}
clientTCP.print(tail);
esp_camera_fb_return(fb);
int waitTime = 10000; // timeout 10 seconds
long startTimer = millis();
boolean state = false;
while ((startTimer + waitTime) > millis()) {
Serial.print(".");
delay(100);
while (clientTCP.available()) {
char c = clientTCP.read();
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;
}
clientTCP.stop();
// Print Information
//Serial.println(getBody);
Serial.println("Photo Sent");
}
else {
getBody = “Connected to api.telegram.org failed.”;
Serial.println(“Connected to api.telegram.org failed.”);
}
return getBody;
}
void handleNewMessages(int numNewMessages) {
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”, “”);
continue;
}
// Print message received
String fromName = bot.messages[i].from_name;
String text = bot.messages[i].text;
Serial.println(numNewMessages + " From " + fromName + " >" + text + " request");
if (text == "Flash") {
FlashState = !FlashState;
Serial.print("FlashState = ");
if (FlashState == true) Serial.println("ON"); else Serial.println("OFF");
// digitalWrite(FLASH_LED_PIN, FlashState);
}
if (text == "Motion") {
MotionState = !MotionState;
Serial.print("MotionState = ");
if (MotionState == true) Serial.println("ON"); else Serial.println("OFF");
// digitalWrite(FLASH_LED_PIN, MotionState);
}
if (text == "Photo") {
sendPhoto = true;
}
if (text == "Readings") {
String readings = getReadings();
bot.sendMessage(chatId, readings, "");
}
if (text == "Start") {
String welcome = "Welcome to the ESP32-CAM Telegram bot.\n";
long rssi = WiFi.RSSI() + 100;
welcome += "Signal Strength: " + String(rssi) + "\n";
welcome += "Photo : takes a new photo\n";
welcome += "Flash : toggle flash LED\n";
if (FlashState == true) welcome += "Flash : ON now\n"; else welcome += "Flash : OFF now\n";
welcome += "Motion : toggle Motion Sensor\n";
if (MotionState == true) welcome += "Motion : ON now\n"; else welcome += "Motion : OFF now\n";
welcome += "Readings : Temp & Humidity\n\n";
welcome += "If Motion is ON, you'll receive a photo whenever motion is detected.\n";
bot.sendMessage(chatId, welcome, "Markdown");
}
}
}
Hi John Pipe. Thank you for sharing the code, very nice from You. It will help a lot. Thanx!
Top! This code was amazing! tks!!!
You may want to try this. To print file name in set up:
Add these lines in set up:
Serial.println(“Program ~ ” + Filename());
Serial.println(“Date Compiled ~ ” + String(DATE));
and this after setup:
String Filename() {
return String(FILE).substring(String(FILE).lastIndexOf(“\”) + 1);
}
Hi John I appreciated your improvements and introductions to my project. except that I get this error due to the instructions entered for printing the WiFi signal strength.
Could you help me. Thank you. Paul
here is the error received during compilation :
F:\ESP32_CAM_TELEGRAM_RCWL_RUI_MOD\Esp32_Cam_RCWL0516_Rui_3\Esp32_Cam_RCWL0516_Rui_3.ino:417: error: unterminated argument list invoking macro “F”
}
exit status 1
Compilation error: unterminated argument list invoking macro “F”
So I order parts for the camera , motion sensor and am waiting for parts. I liked the dome idea but I didn’t find any links to the place to find the dome. Can you provide it, just wanted to make sure I get the correct one so it fits.Also , is it weather resistant? Thanks
Hi Rui/Sara,
Thanks for another great project, has inspired me to get back to learning programming. I plan to build a couple, one as a game camera. From a hardware side, I have no issues, but coding presents its challenges! Is there any way to add servo control so that I am able to pan the camera? Waiting for parts to arrive now.
Thanks
Hi Rui and Sara,
thanks for that project first! I have a minor question: what are the middle 5 contacts for? I received the PCBs as you described and I find now the 5 holes and dont know how to use them. Pls could you tell me what they are for?
Thanks a lot
Greets
Clemens
Hi.
Those are optional.
You can solder header pins if you want to connect additional sensors or outputs and if you want to have an additional power source.
Regards,
Sara
Hi Sara and Rui,
thank you for this great idea!
I want to use it for looking after my expensive plants. For this I want to collect the humidity and temperature data for the whole time. Your webserver (Youtube for ESP32) is nice, but I think it doesn´t save the data for weeks.
Can you tell me, how to use a ESP32-cam-module with a data-collecting-service like thingspeak? The code I wrote causes problems with receiving telegram-messages. Sometimes it freezes and new telegram-messages are not received.
Also I tried to use your modified sketch with a DHT11 and a soil moisture sensor, but unfortunately it doesn´t work 🙁
Thanks for your great job!
Hi Michael.
I think that happens because you are trying to use two services at the same time.
How often do you want to log data?
You can also try using the SD card to save the data as in this tutorial: https://randomnerdtutorials.com/esp32-data-logging-temperature-to-microsd-card/
Regards,
Sara
Thank you so much for this wonderful tutorial. I really love it!!!. 👌👌 All the steps are very clear. Again, thanks very much …..
Hello, can the hc-sr501 sensor be used instead of the one mentioned in the project? Well, I have some difficulties, I used this hc-sr501 and I have problems with the detection, normally what happens is that when I change the sensitivity and time adjustment I get the photo by telegram, but after that I move my hand in front of the sensor and I don’t get more pictures … ???
Greetings. I liked your project very much. Everything is working ! But there is one problem. I set the quality of the pictures for the camera to “high”, and through the telegram server, the photos with low quality come to the phone. I think this is due to the fact that the photo from ESP 32 is transmitted as “PHOTO” and not as a “file”. Therefore, the telegram server reduces its size for easier transmission in chat … Can I fix this somehow ??? So that the photos from the camera are transmitted without compression.
Hi Guys,
Firstly can I say they I appreciate your work. I am learning a lot from your tutorials.
This project however refuses to work for me and I think it is because I am not using an Ai Thinker esp32 cam board but a Wrover Module. Could this be my problem?
If so how easy is it to change the code to work on the Wrover Module?
Regards
Hi Steve.
But the pinout is the same as the AI.Thinker, right?
So, the code should work for your camera. It must be other thing.
What’s exactly the issue you have?
Regards,
Sara
I did get this code to work last night and the camera started sending images to Telegram. My board setting on Arduino IDE was set wrong. Thanks
Hello please update the code, this code wont work unless you add “clientTCP.setCACert(TELEGRAM_CERTIFICATE_ROOT);”
i had telegram connection failed error later i found this and most programs miss this essential line of code..may be it is a necessary in updated library
Hi.
Thanks for telling me about this issue.
I’ve tested it, and you’re right. There must have been some update on the library or on the Telegram app.
We’ll update all tutorials that use Telegram as soon as possible.
Regards,
Sara
Did you find a solution? I need to get this Project done asap. Btw great work thank you for that!
Hi.
Yes.
The codes were updated and are working now.
Regards,
Sara
Got the code working again !!
I am trying out the XYC-WB-DC radar sensor. It works but I get some spurious readings, so I added an LED across the radar sensor and created an OUTPUT pin to drive another LED when motion is detected.
I also have made a few changes to the code in “Start” to:
A: Toggle ON or OFF the Flash.
B: Toggle ON or OFF the MotionState to enable/disable the send Photo
C: Added Signal Strength.
Also my iPhone adds a Capital letter to the commands sent from Telegram, so I changed to code to accept this capital character, “start” is now “Start”.
Motion Sensor Code Changed:
/*
// Read Motion Sensor
if (MotionDetected) {
digitalWrite(SENSOR_LED, HIGH); // Turn SENSOR LED On
bot.sendMessage(chatId, “Motion detected!!”, “”);
Serial.println(“Motion Detected”);
digitalWrite(SENSOR_LED, LOW); // Turn SENSOR LED Off
if (MotionState) {
sendPhotoTelegram();
MotionDetected = false;
}
}
*/
// Alternative Read Motion Sensor
int SensorVal = digitalRead(MOTION_SENSOR);
if (SensorVal == 0) {
MotionDetected = false;
digitalWrite(SENSOR_LED, LOW); // Turn SENSOR LED Off
} else {
MotionDetected = true;
digitalWrite(SENSOR_LED, HIGH); // Turn SENSOR LED On
bot.sendMessage(chatId, “Motion detected”, “”);
Serial.println(“Motion Detected”);
digitalWrite(SENSOR_LED, LOW); // Turn SENSOR LED Off
if (MotionState) {
sendPhotoTelegram();
MotionDetected = false;
}
}
Question: Why use “static void IRAM_ATTR detectsMovement(void * arg)” when you can can define an INPUT on GPIO 13 for the motion sensor?
Keep up the good work.
Hi John, I also had the same problem why not define GPIO13 in order to do something clean. I wonder if you changed the instructions by removing use of .
“static void IRAM_ATTR detectsMovement(void * arg)” ????
If you have modified the program could you send me the correct changes. Thank you
Hi Sara,
used the ESP32 CAM MB USB Programmer:
Ok when reviewing the sketch.
The following error message appears when compiling the sketch (excerpt)
C:\Users\Uwe Dolata\AppData\Local\Arduino15\packages\esp32\tools\esptool_py\2.6.1/esptool.exe –chip esp32 –port COM4 –baud 460800 –before default_reset –after hard_reset write_flash -z –flash_mode dio –flash_freq 80m –flash_size detect 0xe000 C:\Users\Uwe Dolata\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.4/tools/partitions/boot_app0.bin 0x1000 C:\Users\Uwe Dolata\AppData\Local\Arduino15\packages\esp32\hardware\esp32\1.0.4/tools/sdk/bin/bootloader_qio_80m.bin 0x10000 C:\Users\UWEDOL~1\AppData\Local\Temp\arduino_build_435116/Control_ESP32-CAM_with_Telegram.ino.bin 0x8000 C:\Users\UWEDOL~1\AppData\Local\Temp\arduino_build_435116/Control_ESP32-CAM_with_Telegram.ino.partitions.bin
esptool.py v2.6
Serial port COM4
Connecting…….._____….._____….._____….._____….._____….._____….._____
A fatal error occurred: Failed to connect to ESP32: Timed out waiting for packet header
I followed the instructions.
For Telegram I use my already existing bot.
Hi.
Try to hold the programmer IO0 button and then press the ESP32-CAM on-board RST button when you start seeing the dots on the debugging window.
Regards,
Sara
Hi Sara,
Thanks for the quick answer, I could have found it myself in the error list.
Supi worked !!
A new problem:
I can’t get in touch with my telegram bot.
He doesn’t understand the news.
Also, something seems to be wrong with the PCB shield.
Both the motion detector and the BME seem to have a loose contact or my soldering points are not OK. When pulling out / inserting both, the message “Motion detected” plus photo appears on my mobile phone !?
Hi.
The PCB worked just fine for us. Maybe there is some bad solder joint on your PCB? It would be a good idea to check the connections with a multimeter.
As for the Telegram bot, did it ever worked before?
Usually, the problem with this kind of projects is that people don’t insert the right BotToken or the chat user ID. Can you double-check that?
Regards,
Sara
Hi Sara,
see log files of my Iobroker.
Do I have to create a second, new bot?
2021-05-11 19:21:43.997 – warn: telegram.0 (20757) polling_error: ETELEGRAM, ETELEGRAM: 409 Conflict: terminated by other getUpdates request; make sure that only one bot instance is running
2021-05-11 19:21:44.046 – debug: telegram.0 (20757) getMe (reconnect): {“id”:1236242331,”is_bot”:true,”first_name”:”Waschmaschine”,”username”:”Boschwasch_bot”,”can_join_groups”:true,”can_read_all_group_messages”:false,”supports_inline_queries”:false}
Best regards
I forgot to mention that when I close the Telegram Adapter everything works. !!!
Hi guys this is really amazing and made it too for my project…but I want to ask what mode the PIR sensor is in….is it retriggering or non retriggering mode
MIne too. So I have been playing with a time delay.
// Read Motion Sensor
// Delays are to avoid multiple triggers and messages
MotionDetected = digitalRead(MOTION_SENSOR);
if (MotionDetected) Serial.println(“Motion Detected”);
delay(1000); // Wait 1 Second before detecting again
if (MotionState) {
if (MotionDetected) {
digitalWrite(SENSOR_LED, HIGH); // Turn SENSOR LED On
bot.sendMessage(chatId, “Motion detected”, “”);
digitalWrite(SENSOR_LED, LOW); // Turn SENSOR LED Off
sendPhotoTelegram();
MotionDetected = false;
Serial.print(" Please Wait ");
delay(30000); // Wait 30 Seconds before detecting again
Serial.println("> Ready to detect again");
}
}
Hi John,
Just to be clear you are using the PIR sensor in retriggered or repeat trigger mode? You added a delay so it will not trigger continuously. Why not set it in single trigger mode?
Did you have inconsistent triggers with this mode?
Previously you had mentioned you had used the XYC-WB-DC radar sensor. Were you satisfied with the results or is the PIR a better choice?
Have you tried putting the ESP in deep sleep? Adding to my code does not work but it works as a stand alone code.
Thanks,
Joe
Hi,
Thanks for great tutorial. However my ESP32-CAM doesn’t send any message to telegram bot.
Or did I understand wrongly?
Where shall I send those /start, /photo commands? In BotFather or IDBot?
How can I determine that ESP32 is connected to telegram bot.
I receive only “ESP32-CAM IP Address: 192.168.0.90”, that means, it is connecting to my home network but I dont know if it is connected to telegram bot. How can I find out?
Thanks.
Hi.
In your Telegram contacts, you should search for the bot you have created and send those messages to the bot.
Regards,
Sara
Hi,
i have three espcam divices running youre code. Works like a charm. I have found one isseu i can not resolve. If the wifi accespoint reboots the espcam will not reconnect to the wifi. I have to powercycle the espcam to reconnect to the wifi. Can you help resolve this? Is this an isseu with the wifi libary?
Hi.
You can check this tutorial and see if it helps: https://randomnerdtutorials.com/solved-reconnect-esp32-to-wifi/
Regards,
Sara
Hallo,
How can I authorize other users for viewing the ESP32CAM?
Tausend Dank!
Hi.
You can add multiple authorized chat ids or you can create a telegram group.
To learn how to interact with a telegram group, you can follow this tutorial: https://randomnerdtutorials.com/telegram-group-esp32-esp8266/
Regards,
Sara
Danke!
It worked, but, I had to incllude the IDBot as a member, besides my bot. My bot was constantly getting:Unauthorized user, and until I added IDBot and got the groupid, everything worked quiet well.
Do you happen to know why is that?
Hi.
There shouldn’t be necessary to include the IDBot.
You can could have got the telegram group id as shown in this tutorial: https://randomnerdtutorials.com/telegram-group-esp32-esp8266/ unless something has changed.
Regards,
Sara
I follow the tutorial many times for double checking.
Has anyone recently tried to see if something has changed?
Can the code itself have a bug ???:
for (int i = 0; i < numNewMessages; i++) {
String chat_id = String(bot.messages[i].chat_id);
if (chat_id != CHAT_ID){
bot.sendMessage(chat_id, “Unauthorized user”, “”);
continue;
}
Tausendmal Danke!
Hi guys, some time ago I set up the ESP32-CAM from https://randomnerdtutorials.com/esp32-cam-shield-pcb-telegram/ . It worked very well until yesterday when the photos started to be sent to the telegram, let’s say lying down!!! What can it be?
Hi.
What do you mean?
Can you be a little bit more specific?
Regards,
Sara
Thanks. The photos are being received in a ‘landscape’ form and not as before in a ‘portrait’ format. Are let’s say at minus 90º
Hello Thalis,
did you ever get an answer ? I have the same problem. I did build 2 camera setups. The first works fine, the other shows the pictures flipped counter clockwise by 90°.
Kind regards
Horst
Hello, after further research I found comments about different types of cameras, forcing the 90° flip, with no chance to compensate this.
Somebody postet a method, by folding the camera cable, which did work well for me: https://timsblogplace.blogspot.com/2022/01/esp32-cam-rotating-camera.html
Kind regards
Horst
Hi.
Thanks for sharing that trick.
I hope that doesn’t break the camera ribbon.
Regards,
Sara
Hello
Telegram App is blocked in my area and i am using MTProto Proxy on my mobile to have access Telegram. Question is , How is it possible my ESP32-Cam also use the MTProto Proxy to connect Telegram server and access Bot ?
Hi,
i’m testing here with now 3 ESP32Cam’s and have everytime the same problem.
All Cam’s are working with the orig. Software when it goes up with the own AP. i can connect to the cam and all works fine.
when i upload your sketch and the cam connect’s to my wifi, there are always ping failures.
the RSSI is always fine. My Cam and the Notebook are around 2meters of my AP away.
But this issue with all 3 ESP32Cam-Modules.
thanks
pat
Solved!
Hello Sara and Rui. Great project. I would like to add the deep sleep feature but I am having issues placing the code in the right section of your code. I have read on deep sleep from the ESP32 course I bought from you and it explains it very well. I have used the EXT 1 and masked the pins. These are the three lines of code I added. Can you explain to me where I should add the last two lines?
#define BUTTON_PIN_BITMASK 0x2000 // 2^13 in hex
esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK,ESP_EXT1_WAKEUP_ANY_HIGH);
esp_deep_sleep_start();
Thanks very much,
Joe
Hi.
You can put the following line anywhere in the setup as long as it goes before esp_deep_sleep_start():
esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK,ESP_EXT1_WAKEUP_ANY_HIGH);
The other line should be the last thing in your code. When the ESP32 reaches that line of code, it goes into sleep mode and won’t run any other code after that.
Let me know if you need further help.
Regards,
Sara
Hi Sara, Thanks very much for your quick response. The deep sleep works when I run the deep sleep code and I ground the GPIO 13 with a resistor. The ESP32 draws about 2 mA. When I incorporate the deep sleep code to my telegram code and use the same resistor to ground GPIO 13 the current does not decrease and I observe a 0 every second on the serial monitor. This means the ESP is not going to sleep. I added the at the end to the program but it seems to be looping. If I add it outside the brackets the very last entry I get an error. Here is the code w/o SSID info.. Thanks for your support. Joe
#include <WiFi.h>
#include <WiFiClientSecure.h>
#include “soc/soc.h”
#include “soc/rtc_cntl_reg.h”
#include “esp_camera.h”
#define uS_TO_S_FACTOR 1000000 /* Conversion factor for micro seconds to seconds /
#define TIME_TO_SLEEP 10 / Time ESP32 will go to sleep (in seconds) */
//CAMERA_MODEL_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
#define BUTTON_PIN_BITMASK 0x2000 // 2^13 in hex
int gpioPIR = 13; //PIR Motion Sensor
void setup()
{
esp_sleep_enable_ext1_wakeup(BUTTON_PIN_BITMASK,ESP_EXT1_WAKEUP_ANY_HIGH);
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
Serial.begin(115200);
delay(10);
WiFi.mode(WIFI_STA);
Serial.println(“”);
Serial.print(“Connecting to “);
Serial.println(ssid);
WiFi.begin(ssid, password);
long int StartTime=millis();
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
if ((StartTime+10000) < millis()) break;
}
Serial.println(“”);
Serial.println(“STAIP address: “);
Serial.println(WiFi.localIP());
Serial.println(“”);
if (WiFi.status() != WL_CONNECTED) {
Serial.println(“Reset”);
ledcAttachPin(4, 3);
ledcSetup(3, 5000, 8);
ledcWrite(3,10);
delay(200);
ledcWrite(3,0);
delay(200);
ledcDetachPin(3);
delay(1000);
ESP.restart();
}
else
{
ledcAttachPin(4, 3);
ledcSetup(3, 5000, 8);
for (int i=0;i<5;i++) {
ledcWrite(3,10);
delay(200);
ledcWrite(3,0);
delay(200);
}
ledcDetachPin(3);
}
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(psramFound())
{
config.frame_size = FRAMESIZE_VGA;
config.jpeg_quality = 10; //0-63 lower number means higher quality
config.fb_count = 2;
}
else
{
config.frame_size = FRAMESIZE_QQVGA;
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);
delay(1000);
ESP.restart();
}
sensor_t * s = esp_camera_sensor_get();
s->set_framesize(s, FRAMESIZE_XGA);
}
void loop()
{
pinMode(gpioPIR, INPUT_PULLUP);
int v = digitalRead(13);
Serial.println(v);
if (v==1)
{
alerts2Telegram(token, chat_id);
delay(10000);
}
delay(1000);
}
//esp_deep_sleep_start();
String alerts2Telegram(String token, String chat_id)
{
const char* myDomain = “api.telegram.org”;
String getAll=””, getBody = “”;
camera_fb_t * fb = NULL;
fb = esp_camera_fb_get();
if(!fb)
{
Serial.println(“Camera capture failed”);
delay(1000);
ESP.restart();
return “Camera capture failed”;
// esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);//deep sleep
// esp_deep_sleep_start();
}
esp_deep_sleep_start();
WiFiClientSecure client_tcp;
if (client_tcp.connect(myDomain, 443))
{
Serial.println(“Connected to ” + String(myDomain));
String head = "--India\r\nContent-Disposition: form-data; name=\"chat_id\"; \r\n\r\n" + chat_id + "\r\n--India\r\nContent-Disposition: form-data; name=\"photo\"; filename=\"esp32-cam.jpg\"\r\nContent-Type: image/jpeg\r\n\r\n";
String tail = "\r\n--India--\r\n";
uint16_t imageLen = fb->len;
uint16_t extraLen = head.length() + tail.length();
uint16_t totalLen = imageLen + extraLen;
client_tcp.println("POST /bot"+token+"/sendPhoto HTTP/1.1");
client_tcp.println("Host: " + String(myDomain));
client_tcp.println("Content-Length: " + String(totalLen));
client_tcp.println("Content-Type: multipart/form-data; boundary=India");
client_tcp.println();
client_tcp.print(head);
uint8_t *fbBuf = fb->buf;
size_t fbLen = fb->len;
for (size_t n=0;n<fbLen;n=n+1024)
{
if (n+1024<fbLen)
{
client_tcp.write(fbBuf, 1024);
fbBuf += 1024;
}
else if (fbLen%1024>0)
{
size_t remainder = fbLen%1024;
client_tcp.write(fbBuf, remainder);
}
}
client_tcp.print(tail);
esp_camera_fb_return(fb);
int waitTime = 10000; // timeout 10 seconds
long startTime = millis();
boolean state = false;
while ((startTime + waitTime) > millis())
{
Serial.print(".");
delay(100);
while (client_tcp.available())
{
char c = client_tcp.read();
if (c == '\n')
{
if (getAll.length()==0) state=true;
getAll = "";
}
else if (c != '\r')
getAll += String(c);
if (state==true) getBody += String(c);
startTime = millis();
}
if (getBody.length()>0) break;
}
client_tcp.stop();
Serial.println(getBody);
}
else {
getBody = “Connection to telegram failed.”;
Serial.println(“Connection to telegram failed.”);
}
return getBody;
//esp_deep_sleep_start();
}
//esp_deep_sleep_start();
Hi.
It is very difficult to read code like this. Please, post your code in Pastebin or GitHub and then, share the corresponding link.
Regards,
Sara
Hi again.
When i take a photo with command by Telegram, i see the snapshot is not actual.
so the snapshot is taken a couple of seconds before.
is there a snapshot buffer or else?
thanks
pat
Hi and thanks for this great project.
i would like to used a Normally open micro Switch to trigger the even of sending the photo.
how can i use it instead of the motion sensor.
should i connect the 2 leads from the microswitch to where the yellow and black connections are on the motion sensor.
Hi.
Yes. It is like reading a button.
Regards,
Sara
thanks very much for your help, really appreciated. have a great day and be safe.
one question about image resolution
how can it be improve it, I am getting resolution of 400×296 on the pictures.
try to change config.jpeg_quality but does not make any difference.
read some where in this long thread to change from 16 to 32bit but don’t know where to do those changes.
just got today this board and was just plug and play on windows 10
https://www.amazon.com/gp/product/B09263L8DQ/
any help would be really appreciated.
best regards
Hi.
To learn more about changing the settings, you can read this article: https://randomnerdtutorials.com/esp32-cam-ov2640-camera-settings/
Regards,
Sara
thanks for the link really useful info.
the only thing that can be telegram reducing the size.
because on the webserver i can set the resolutions and works good up to 1600 x 1200
another question how can i get to send this
http://USER:PASS@CAM IP/cgi-bin/snapshot.cgi
snap shot from my dahua ip camera to telegram.
Can we work this in a P2P set up ? where the phone with telegram connects to the ESP32 AP and then communicates the readings ?
Hello,
I am building this project. the ESP32 cam connects to the network, also, it connects to the telegram bot. however it does not respond to any further command from the bot such as /flash or /photo. I checked the serial monitor as well, the last status I see is “Handle New messages: 1” /start. But any further command from bot is not registered to the esp32
please help in getting it working .
Hi Sara,
Thanks for wonderful website and so much of educational material!
In terms of this project however I have some critique . As Andreas mentioned Esp32-cam seems to “store buffer” and display old photos so whole idea of motion triggering makes no sense.. Somebody will trigger camera but you will receive old photo without whoever triggered it… Assuming someone could disconnect camera at this point you will never know if this was false alarm or who triggered it. IT seems like major floor to this project..
Unless this buffer problem is specific to certain esp32-cam models.. But all I tried behaved same way also is present on ESP32 web server example.
I wasn’t able to overcome this I really hope you could check and fix it somehow. Many thanks
ps sorry I posted it on wrong project initially
Hi, I have the same issue with two of my esp32-cams. Hope, Sara will help us there.
Hi Sara.
I have the same issue of my esp32cam.
I have the same problem….i see pics that are MINUTES old, not the “actual” photo.
Hi,
Had the same issue, resolved by adding config.grab_mode = CAMERA_GRAB_LATEST;
to the end of the camera config section.
THAT is a very useful tip. Thanks a bunch 🙂
Can I have ESP32-CAM to do live web streaming and telegram take photo, control output and motion notification to be put together? Cause I try to combine the code for them both, it did not work.
Hi.
I’m not sure.
I don’t think you can take pictures and live streaming at the same time without crashing the board, but I might be wrong…
Regards,
Sara
how to set it to take pictures with flash ,please
Hello Sara,
Wonderful and meticulosly written instructions. I especially appreciate the section where you also explain the working of the code.
One question – can you give any hint to integrate this with MQTT brokers – like sending notification to the broker, say using PubSubClient. Any more hints would be appreciated.
Hi.
We have several tutorials where you can learn about MQTT.
Here’s a link to all our MQTT tutorials:
– https://randomnerdtutorials.com/?s=mqtt
Scroll down and check the ones that might be suitable to integrate with this tutorial and the project you have in mind.
Regards,
Sara
Hi, I have this project wired up to a project board, using a bmp 280, instead of the bme 280 and have double checked my wiring. Everything works great, with one exception. I’m only getting motion detected when I first plug the unit in, and when I request readings, it will return both values, as 0, and then immediately after, I get motion detected message. So somehow it’s not reading the sensor values, and triggering the motion upon that request…. Any ideas? I’m going to try a different esp32 ai thinker just to rule out the board, in the meantime.
HI. Could you please explain this portion of the sketch :
if (getBody.length()>0) break;
}
clientTCP.stop();
Serial.println(getBody);
}
else {
getBody=”Connected to api.telegram.org failed.”;
Serial.println(“Connected to api.telegram.org failed.”);
Becaus eI’m having the message “Connected to api.telegram.org failed”
Thanks in advance
Because I’m having ….
Hi. I have 2 internet connections at home(so 2 Internet Service Providers). How it comes every thing is working flawlessly with one ISP and with the second one i’m getting the message : “connected to api.telegram.org failed” ?
What could be the problem and how to solve this issue?
Thanks
hi Sara, I ask you if instead of the pir you can use the rcwl-0516 module always using esp32 cam ? Thank you . A warm greeting
Hello thank for this beautiful tutorial;
My esp 32 cam doesn’t take picture at real time
When sendPhotoTelegram is called when i request /photo, the cam take photo few minute before it’s real position..
How can i fix this problem
Papis !
Good morning, I would kindly ask Rui or Sara for a little help. I tried to modify this project by replacing the PIR with RCWL-0516, but I can’t get it to work. Would you be kind enough to propose the solution perhaps with a new project. Thank you with all my heart. Paul
I replaced the PIR with RCWL-0516 and got it to work without any problems.
Double check your wiring.
Thanks Eddy, now it works for me too, except that the RCWL-0516 module, even in the absence of movement or presence, always sends images. In practice, it always senses the presence of movement even if it is in a closed room where no one enters. does it do it to you too? thanks for your time. Greeting . Paul
This is because your rcwl is close to WiFi signal. Try to put it as far as possible from WiFi signals to avoid interferences. You can also decrease its sensitivity (use Google for that).
Hi Eddy, thank you for your willingness to help me, but I tried to upload a simple sketch like the one proposed by Rui and Sara’s site with a mega Arduino positioned in the same point where I had placed the project with the esp32 cam, and I have to tell you that the scwl-0516 sensor works very well without making non-existent readings continuously. I will do the test you suggest by placing the esp32 cam away from the WiFi router. Could you please suggest to me if it is worth installing a photoresistor? another thing Eddy, to mount an external antenna on the esp32 cam, do you have to cut a track on the printed circuit board? Thank you . Greetings from Bari Italy.
I have never used a photo resistor on my rcwl.
To install an external antenna you have to change the position of the small resistor located near the antenna connector or you can desolder this resistor and put a bridge between two solder pads (there is a lot of tutorials on how to do this in the net.).
Another thing to try to avoid false triggering of your rcwl is to switch its position upside downside.
Hi Eddy, I did what you advised me to do, keep the esp32 cam away from the wifi router. Nothing to do continuously sends non-existent movements. Yet with Arduino Mega it works very well, do you have any ideas? furthermore the wifi range is really limited, by moving the esp to a room 6 meters away it no longer picks up wifi. ? Does it improve much with an antenna? I don’t want to get discouraged and abandon this project. If you can give me some other tips. Maybe if you can send me a photo of your wiring to my email address. Thank you
Help, I connected RCWL-0516 instead of the PIR, but after many attempts, esp32 cam continuously sends photos endlessly without having detected a movement. If anyone has solved the problem brilliantly, he could kindly help me. Thank you
Hi.
First test your RCWL-0516 with a simple example sketch to check if everything is working as expected.
Regards,
sara
Hi Sara, as in my previous comments, the RCWL-0516 module with a simple Skizzo and mega Arduino works very well. This module connected with esp32 cam which has the Wifi module on board creates problems. Try it too and let me know. I’m opting for another sensor of the Radar HW-MS03 type, on the web someone who tried it wrote that he had no problems.
i have the same issue that the esp32 cam continuously sends photos endlessly without having detected a movement i make a unit test for the PIR sensor and it works fine and i don’t know what is the problem
Hi.
Can you try to use another pin?
Regards,
Sara
i solve the problem by make the pin of PIR sensor pulled_down
Hi Ahmed, could you be more precise about the solution that works, I don’t understand what “pulling down the PIR sensor pin” means. Thank you
Can one use the same code for an ESP32-S or must it be the regular ESP32?
Does Telegram need to be open at all times for the motion sensor picture capture to work?
Any ESP32
No, Telegram client can be closed.
User will be notified (as per setting) when even occurs
Does the camera and the telegram app(Phone) needs to be on the same network? Or can it be access from anywhere in the world??
Hi.
It can be accessed from anywhere.
Regards
Sara
Greetings Sarah and Rui.
A project that I had been putting off for a long time and was now struggling a little. Well it works perfectly, great tutorial.
I don’t know if this will be advertising, but I bought a programmer from the Czech “laskakit.cz”, programming is simplified, everything is included in the board.
Thank you and I wish you a lot of strength in your work and a nice summer.
Hello,
I have to raise the flag and just can’t get any further.
I have invested a lot of time in getting the program to run with a Wrover board, but I keep getting the same error.
The following situation:
I upload the sketch to a Wrover board. Adjusted with the correct camera setting for the Wrover board. Adjusted with my wifi and telegram settings. So far so good.
However, when uploading and starting the board, a picture is automatically sent to Telegram every minute or so, even though there has been no movement.
I have uploaded a simple motion sketch and tested whether the motion detector is defective. but it is not. it works perfectly with the simple sketch.
I uploaded a simple sketch to upload photos to Telegram via /photo. it works perfectly.
However, when I use this sketch above images are sent to Telegram continuously, regardless of whether there is movement or not.
Does anyone have the same problem and possibly a solution?
I have used several boards and PIR sensors. I keep getting the error. I suspect that the camera setting influences the PIR or vice versa.
I have another board with AI-Thinker where the sketch works perfectly. However, I would like to use the Wrover board.
thanks in advance !!
BR
Silvio
Great project,
I didn’t see this in the comments, but anyway to code camera to rotate from portrait to landscape, 90 degree shift?
Thanks,
Tim
Hello.
In the sketch section
camera_config_t config;
it is stated
config.pin_sscb_sda = SIOD_GPIO_NUM;
config.pin_sscb_scl = SIOC_GPIO_NUM;
V VS Code points out that this is outdated (deprecated) and needs to be changed to
config.pin_sccb_sda = SIOD_GPIO_NUM;
config.pin_sccb_scl = SIOC_GPIO_NUM;
VS Code PlatformIO
Hello.
I’m sorry, but the main part of the text fell out. So a correction.
In the sketch section
camera_config_t config;
it is stated
config.pin_sscb_sda = SIOD_GPIO_NUM;
config.pin_sscb_scl = SIOC_GPIO_NUM;
In VS Code, PlatformIO lists the error as obsolete (deprecated) . Offers repair
config.pin_sccb_sda = SIOD_GPIO_NUM;
config.pin_sccb_scl = SIOC_GPIO_NUM;
Thanks,
Dusan.