Home Unlabelled How to Send Images from an ESP32-S3 Camera to Telegram
How to Send Images from an ESP32-S3 Camera to Telegram
By Aishwarya At 8/14/2026 11:51:00 PM 0
How to Send Images from an ESP32-S3 Camera to Telegram
Building an IoT camera that can send photos directly to your phone is a great way to learn how embedded devices, Wi-Fi, cameras, and cloud APIs work together.
In this project, we will use an ESP32-S3 camera to capture a JPEG image and send it directly to a Telegram bot. The entire process happens over Wi-Fi, so you don't need a computer, ngrok, or a separate web server.
The final result is simple:
ESP32-S3 Camera
│
│ Wi-Fi
▼
Telegram Bot API
│
▼
Telegram App
│
▼
📷 Photo
Let's build it step by step.
What We Are Building
The ESP32-S3 will:
Connect to a Wi-Fi network.
Initialize the camera.
Capture a JPEG image.
Connect securely to Telegram.
Upload the image to a Telegram bot.
Send the photo to our Telegram chat.
Once everything is working, we can later extend the project to capture images when motion is detected, when a button is pressed, or at regular intervals.
Components Required
For this project, you need:
ESP32-S3 camera board
Camera module
USB cable
Wi-Fi network
Telegram account
Telegram mobile app
Arduino IDE
I'm using an ESP32-S3 camera with the ESP32S3_EYE camera configuration.
Creating the Telegram Bot
The first step is to create a Telegram bot.
Open Telegram and search for BotFather.
Start a conversation with BotFather and send:
/newbot
BotFather will ask you for a name.
For example:
ESP32 Camera
Next, it will ask for a username.
For example:
Imagesp32cam_bot
The username must end with bot.
After creating the bot, BotFather will provide an HTTP API token.
It will look similar to:
123456789:AAxxxxxxxxxxxxxxxxxxxxxxxx
Keep this token private. Anyone who has the token can control your bot.
Getting the Telegram Chat ID
Next, we need to tell Telegram where the ESP32 should send the photo.
Open your newly created bot and press Start.
Send it a message:
hello
Now we can use the Telegram Bot API to find the chat ID.
Open a browser and enter:
https://api.telegram.org/botYOUR_BOT_TOKEN/getUpdates
Replace YOUR_BOT_TOKEN with your actual token.
The response will contain information similar to:
{
"ok": true,
"result": [
{
"message": {
"chat": {
"id": 123456789
}
}
}
]
}
The value of:
chat.id
is your Telegram Chat ID.
For example:
123456789
Keep this number for the ESP32 program.
Setting Up the ESP32-S3
Open Arduino IDE and make sure the ESP32 board package is installed.
Select your ESP32-S3 board from:
Tools → Board
For the camera configuration, select:
#define CAMERA_MODEL_ESP32S3_EYE
and include the corresponding camera pin definitions:
#include "camera_pins.h"
Using the correct camera model is extremely important. If the camera pin configuration doesn't match your hardware, the camera may fail to initialize or the ESP32 may continuously reboot.
Required Libraries
One advantage of this project is that we don't need a special Telegram library.
The program uses:
#include "esp_camera.h"
#include
#include
These are provided by the ESP32 Arduino environment.
esp_camera.h handles the camera, WiFi.h handles the network connection, and WiFiClientSecure.h allows us to communicate with Telegram over HTTPS.
Configuring Wi-Fi and Telegram
Add your Wi-Fi credentials:
const char* WIFI_SSID = "YourWiFi";
const char* WIFI_PASSWORD = "YourPassword";
Then add your Telegram bot token:
const char* BOT_TOKEN = "YOUR_BOT_TOKEN";
And your Chat ID:
const char* CHAT_ID = "123456789";
For security, don't publish your real bot token in source code repositories or blog posts.
Initializing the Camera
The ESP32 camera library uses a camera_config_t structure to configure the camera.
For example:
config.pixel_format = PIXFORMAT_JPEG;
config.frame_size = FRAMESIZE_QVGA;
config.jpeg_quality = 12;
We're starting with QVGA:
320 × 240
This is a good starting point because it produces relatively small JPEG files and reduces memory usage.
If your ESP32-S3 has PSRAM, you can later increase the resolution.
Capturing an Image
Once the camera is initialized, capturing an image is very simple:
camera_fb_t* fb = esp_camera_fb_get();
The returned frame buffer contains the JPEG image.
The image data is available through:
fb->buf
and the image size is:
fb->len
For example:
Serial.printf("Image size: %u bytes\n", fb->len);
After we're finished with the image, we must return the buffer:
esp_camera_fb_return(fb);
This is important because the camera needs the buffer for future captures.
Sending the Image to Telegram
Telegram provides a Bot API method called sendPhoto.
The ESP32 sends an HTTPS POST request to:
https://api.telegram.org/bot/sendPhoto
The request contains two important fields:
chat_id
photo
The image is uploaded as multipart/form-data.
The ESP32 therefore doesn't need to upload the image to another server first.
The process is:
Camera
↓
JPEG buffer
↓
HTTPS POST
↓
Telegram Bot API
↓
Telegram chat
Complete ESP32-S3 Code
Here is the complete example:
#include "esp_camera.h"
#include
#include
#define CAMERA_MODEL_ESP32S3_EYE
#include "camera_pins.h"
// Wi-Fi
const char* WIFI_SSID = "YOUR_WIFI";
const char* WIFI_PASSWORD = "YOUR_PASSWORD";
// Telegram
const char* BOT_TOKEN = "YOUR_BOT_TOKEN";
const char* CHAT_ID = "YOUR_CHAT_ID";
bool sendPhotoToTelegram(camera_fb_t* fb)
{
if (fb == NULL) {
Serial.println("Image buffer is NULL");
return false;
}
WiFiClientSecure client;
// For initial testing
client.setInsecure();
Serial.println("Connecting to Telegram...");
if (!client.connect("api.telegram.org", 443)) {
Serial.println("Telegram connection failed");
return false;
}
String boundary = "----ESP32CameraBoundary";
String head =
"--" + boundary + "\r\n"
"Content-Disposition: form-data; "
"name=\"chat_id\"\r\n\r\n" +
String(CHAT_ID) +
"\r\n"
"--" + boundary + "\r\n"
"Content-Disposition: form-data; "
"name=\"photo\"; filename=\"esp32.jpg\"\r\n"
"Content-Type: image/jpeg\r\n\r\n";
String tail =
"\r\n--" + boundary + "--\r\n";
size_t contentLength =
head.length() +
fb->len +
tail.length();
String request =
"POST /bot" + String(BOT_TOKEN) +
"/sendPhoto HTTP/1.1\r\n"
"Host: api.telegram.org\r\n"
"Content-Type: multipart/form-data; boundary=" +
boundary + "\r\n"
"Content-Length: " +
String(contentLength) + "\r\n"
"Connection: close\r\n\r\n";
client.print(request);
client.print(head);
client.write(fb->buf, fb->len);
client.print(tail);
Serial.println("Image uploaded.");
unsigned long timeout = millis();
while (client.connected() &&
millis() - timeout < 10000) {
while (client.available()) {
String line =
client.readStringUntil('\n');
Serial.println(line);
timeout = millis();
}
}
client.stop();
return true;
}
bool initCamera()
{
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;
config.frame_size = FRAMESIZE_QVGA;
config.jpeg_quality = 12;
if (psramFound()) {
Serial.println("PSRAM found");
config.fb_location = CAMERA_FB_IN_PSRAM;
config.fb_count = 2;
config.grab_mode = CAMERA_GRAB_LATEST;
} else {
Serial.println("PSRAM not found");
config.fb_location = CAMERA_FB_IN_DRAM;
config.fb_count = 1;
config.grab_mode = CAMERA_GRAB_WHEN_EMPTY;
}
esp_err_t err = esp_camera_init(&config);
if (err != ESP_OK) {
Serial.printf(
"Camera initialization failed: 0x%x\n",
err
);
return false;
}
sensor_t* sensor = esp_camera_sensor_get();
if (sensor == NULL) {
return false;
}
sensor->set_framesize(
sensor,
FRAMESIZE_QVGA
);
sensor->set_vflip(
sensor,
1
);
return true;
}
void setup()
{
Serial.begin(115200);
delay(2000);
Serial.println();
Serial.println("==============================");
Serial.println("ESP32-S3 TELEGRAM CAMERA");
Serial.println("==============================");
WiFi.mode(WIFI_STA);
WiFi.begin(
WIFI_SSID,
WIFI_PASSWORD
);
Serial.print("Connecting to Wi-Fi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println();
Serial.println("Wi-Fi connected");
Serial.print("ESP32 IP: ");
Serial.println(WiFi.localIP());
if (!initCamera()) {
Serial.println(
"Camera initialization failed"
);
while (true) {
delay(1000);
}
}
Serial.println("Camera ready");
camera_fb_t* fb =
esp_camera_fb_get();
if (fb == NULL) {
Serial.println(
"Camera capture failed"
);
return;
}
Serial.printf(
"Captured image: %u bytes\n",
fb->len
);
sendPhotoToTelegram(fb);
esp_camera_fb_return(fb);
Serial.println("Photo sent!");
}
void loop()
{
}
Uploading the Code
Before uploading, replace:
YOUR_WIFI
with your Wi-Fi name.
Replace:
YOUR_PASSWORD
with your Wi-Fi password.
Replace:
YOUR_BOT_TOKEN
with the token from BotFather.
Finally, replace:
YOUR_CHAT_ID
with your Telegram Chat ID.
Upload the sketch to your ESP32-S3 and open the Serial Monitor at:
115200 baud
Expected Serial Output
If everything is working, you should see something similar to:
==============================
ESP32-S3 TELEGRAM CAMERA
==============================
Connecting to Wi-Fi......
Wi-Fi connected
ESP32 IP: 192.168.31.193
PSRAM found
Camera ready
Captured image: 18342 bytes
Connecting to Telegram...
Image uploaded.
Photo sent!
You should then receive the image in your Telegram chat.
Sending Images Automatically
The current example sends one image when the ESP32 starts.
We can easily change this to send an image every 30 seconds:
void loop()
{
camera_fb_t* fb = esp_camera_fb_get();
if (fb != NULL) {
Serial.printf(
"Captured: %u bytes\n",
fb->len
);
sendPhotoToTelegram(fb);
esp_camera_fb_return(fb);
}
delay(30000);
}
Now the ESP32 behaves like a simple remote camera:
Capture
↓
Send to Telegram
↓
Wait 30 seconds
↓
Capture again
↓
Send again
What's Next?
Once the basic system is working, there are many ways to improve it.
Motion Detection
Connect a PIR sensor:
Motion detected
↓
ESP32-S3
↓
Capture photo
↓
Telegram
↓
📷 Motion detected
Button-Controlled Camera
Add a push button:
Button pressed
↓
Capture image
↓
Send to Telegram
This could be used as a simple Wi-Fi doorbell.
Security Camera
We can also combine the camera with motion detection and send a Telegram notification whenever someone enters the camera's field of view.
Conclusion
In this project, we built a simple IoT camera using an ESP32-S3 and Telegram.
The ESP32 captures a JPEG image and communicates directly with Telegram's Bot API over Wi-Fi. Because the image is sent directly to Telegram, there is no need for ngrok, port forwarding, or a separate computer running a server.
The complete architecture is:
┌──────────────┐
│ ESP32-S3 │
│ Camera │
└──────┬───────┘
│
│ Wi-Fi
▼
┌──────────────┐
│ Telegram │
│ Bot API │
└──────┬───────┘
│
▼
┌──────────────┐
│ Telegram │
│ App │
└──────────────┘
│
▼
📷 Image
This provides a solid foundation for more advanced ESP32 camera projects such as motion alerts, smart doorbells, remote monitoring, and security cameras.
Subscribe to:
Post Comments
(
Atom
)

Post a Comment