In this project we’ll create a Telegram bot for the ESP32-CAM AI-Thinker board with a PIR motion sensor, a BME280 temperature (humidity and pressure sensor). For this we will use a ready-made PCB shield, credit goes to Rui Santos (link:https://github.com/RuiSantosdotme/ESP32CAMShieldTelegram/raw/master/Gerber_PCB_2020-07-07_10-25-47_2020-07-07_11-49-04.zip), orderable at JLCPCB, to attach components like sensor and camera. Later you will find a generic triple adapter here, as you see in the picture, which allows to use other sensors as well. For the moment 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.
Project OverviewThis project consists of two parts:
1. Creating Telegram Bot
2. Programming the PCB using Arduino IDE.
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.
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.
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.
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.
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 LibraryTo 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 LibraryYou 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 LibraryIn 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 theBME280 Sparkfun libraryinstead 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.
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.
#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 = "MSI GF63";
const char* password = "hwhkolkata";
// 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 = "********";
// Initialize Telegram BOT
String BOTtoken = "************************";
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_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
// 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");
}
}
}
Upload Code to the ESP32-CAMAfter making the necessary changes, upload the code to your ESP32-CAM (before connecting the shield).
In this project we use ESP32-CAM-MB Micro USB Download Module for ESP32 CAM Development Board.
Attach ESP32 Cam module with this ESP32-CAM-MB Micro USB Download Module. The description of the ESP32-CAM-MB Micro USB Download Module given below.
After attaching the camera module to the download module, connect the micro USB cable to the download module (as per the above figure). Connect it with computer. Open Arduino IDE, then select the COM port. Then upload the code (attach above).
DemonstrationWith 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.
Comments
Please log in or sign up to comment.