ESP32-CAM Telegram : envoyer une photo dès qu'il y a un mouvement
Envoie des photos de l'ESP32-CAM sur Telegram : câblage du PIR et broche libre, commande /foto, flash, deep sleep sur batterie, photos de nuit et code complet.
Quand tu mets en place un bot ESP32-CAM Telegram, la carte prend une photo dès qu'elle détecte un mouvement et te l'envoie sur ton téléphone en quelques secondes. Pas besoin d'installer une application, de louer un serveur ni d'ouvrir un port sur ta box : la carte envoie la photo directement aux serveurs de Telegram, qui te la livre sous forme de notification instantanée. Avec le même bot, tu peux taper /foto pour voir ta porte d'entrée quand tu veux, allumer ou éteindre le flash et désactiver l'alarme.
Dans ce guide, tu verras pas à pas sur laquelle des rares broches libres de l'ESP32-CAM brancher un capteur PIR, comment la photo est envoyée à Telegram, le code complet piloté par commandes et la bonne façon d'utiliser le flash. Ensuite, tu feras tourner la carte plusieurs jours sur batterie grâce au deep sleep, tu prendras des photos de nuit avec un éclairage IR et tu verras où ce montage atteint ses limites comme caméra de surveillance. Si tu n'as pas encore configuré la carte ni téléversé ton premier programme, lis d'abord Utiliser l'ESP32-CAM.

Comment fonctionne un système photo ESP32-CAM avec Telegram ?
La chaîne compte quatre étapes. Le capteur PIR remarque un changement de la chaleur émise par un corps humain et met sa broche OUT à 3,3 V. Quand l'ESP32 voit ce front montant, il demande une image JPEG à la caméra OV2640, qui est écrite dans la PSRAM de 4 Mo de la carte. L'ESP32 ouvre ensuite une connexion HTTPS chiffrée et envoie une requête POST au format multipart/form-data à la méthode sendPhoto de l'API des bots Telegram ; c'est la même méthode qu'un navigateur utilise pour envoyer un fichier. Telegram ajoute la photo à ta conversation et ton téléphone affiche une notification. Les commandes vont dans l'autre sens : chaque seconde, l'ESP32 demande avec getUpdates s'il y a de nouveaux messages et, si /foto est arrivé, il fait le même envoi pour ta demande.
| Composant | Caractéristique | Remarque |
|---|---|---|
| ESP32-CAM (AI-Thinker) | OV2640, 4 Mo de PSRAM | La version avec PSRAM gère sans peine les photos XGA |
| ESP32-CAM-MB ou convertisseur USB-TTL | CH340 / CP2102 | Pour téléverser le code ; la carte n'a pas d'USB |
| Capteur PIR | HC-SR501 (5 V) ou AM312 (3,3 V) | La sortie OUT est en 3,3 V et se branche directement sur l'ESP32 |
| Alimentation | 5 V, au moins 2 A | Le Wi-Fi et le flash consomment beaucoup en même temps |
| Condensateur électrolytique | 470–1000 µF | Entre 5V et GND, près de la carte |
| En option | Accu 18650 + convertisseur élévateur 5 V, projecteur IR 850 nm | Pour le fonctionnement sur batterie et les photos de nuit |
Câblage du capteur PIR et choix d'une broche libre
L'ESP32-CAM a 16 broches sur ses bords, mais la caméra utilise à elle seule 15 GPIO et le lecteur microSD en partage six autres. La plupart de celles qui restent sont des broches de démarrage (strapping) ou servent au port série. Comme ce projet n'utilise pas de carte SD, les lignes SD sont libres et la plus sûre est GPIO 13 : elle n'influence pas le démarrage, possède une résistance de pull-down interne et, comme c'est une broche RTC, elle peut réveiller la carte du deep sleep. Le tableau ci-dessous compare aussi les autres candidates.
| Broche | Sans carte SD | Réveille du deep sleep ? | Remarque |
|---|---|---|---|
| GPIO 13 | Libre | Oui (ext0) | Le meilleur choix pour le PIR |
| GPIO 14 | Libre | Oui | Peut sortir un bref signal PWM au démarrage ; aucun souci en entrée |
| GPIO 15 | Libre | Oui | Broche de démarrage ; à LOW, elle coupe les messages de démarrage |
| GPIO 2 | Libre | Oui | Broche de démarrage ; ne doit pas être à HIGH pendant le téléversement |
| GPIO 12 | Libre | Oui | Si elle est à HIGH au démarrage, la carte ne démarre pas ; à éviter |
| GPIO 4 | LED de flash | Oui | Réservée au flash ; ne l'utilise pas pour autre chose |
| GPIO 3 (U0R) | RX série | Non | Seulement si tu n'as pas besoin du port série |
| GPIO 16 | PSRAM | Non | Ne jamais l'utiliser |
| Broche du PIR | Broche de l'ESP32-CAM | Description |
|---|---|---|
| VCC | 5V | Le HC-SR501 a besoin d'au moins 4,5 V ; avec un AM312, utilise 3V3 |
| OUT | GPIO 13 | 3,3 V en cas de mouvement, 0 V sinon |
| GND | GND | Masse commune |
Des deux potentiomètres du HC-SR501, tourne celui de la durée (Tx) sur la position la plus courte (à fond dans le sens antihoraire, environ 3 secondes) ; c'est le code qui gérera l'attente. Laisse le cavalier de déclenchement sur H (redéclenchement) et règle la sensibilité entre 3 et 7 mètres selon ton besoin. Après la mise sous tension, le capteur met environ une minute à s'habituer à son environnement et se déclenche au hasard pendant ce temps ; c'est pourquoi le code ignore les 60 premières secondes. Le fonctionnement détaillé du capteur est expliqué dans Capteur de mouvement PIR.
Préparer le bot Telegram : token et chat ID
- Ouvre @BotFather dans Telegram, tape /newbot et donne à ton bot un nom et un nom d'utilisateur qui se termine par bot.
- Copie le token au format 1234567890:AAE... que BotFather te donne ; il ira dans la ligne BOT_TOKEN du code.
- Pour connaître ton numéro d'utilisateur, envoie /start à @userinfobot ; la valeur Id renvoyée est ton CHAT_ID.
- Ouvre ton propre bot et envoie /start une fois ; Telegram ne permet pas à un bot d'écrire à quelqu'un avec qui il n'a jamais échangé.
- Dans le gestionnaire de bibliothèques de l'Arduino IDE, installe UniversalTelegramBot (Brian Lough) et ArduinoJson.
- Dans Outils > Type de carte, choisis AI Thinker ESP32-CAM et vérifie que le Partition Scheme est Huge APP (3MB No OTA/1MB SPIFFS).
Envoyer une photo sur Telegram : comment fonctionne sendPhotoByBinary ?
La fonction sendPhotoByBinary() de la bibliothèque UniversalTelegramBot envoie la photo comme un fichier. Elle transmet d'abord l'en-tête de la requête et le champ du chat ID, puis demande les données à trois petites fonctions que tu fournis : veriVarMi() indique s'il reste un morceau à envoyer, sonrakiTampon() donne l'adresse des données en mémoire et tamponBoyu() leur taille en octets. Comme le tampon d'image de la caméra est déjà un seul JPEG, le code transmet la photo entière d'un coup et baisse le drapeau. Une photo de 1024×768 en qualité 12 pèse entre 50 et 120 Ko selon la scène et part en 1 à 2 secondes avec un bon Wi-Fi. Telegram accepte les photos jusqu'à 10 Mo, tu n'approcheras donc jamais la limite de taille ; la vraie limite, c'est la durée d'envoi.
Code complet : commande /foto, contrôle du flash et alarme de mouvement
1#include <WiFi.h>
2#include <WiFiClientSecure.h>
3#include <UniversalTelegramBot.h>
4#include <ArduinoJson.h>
5#include <time.h>
6#include "esp_camera.h"
7
8const char* WIFI_SSID = "AgAdi"; // Wi-Fi adın (yalnız 2,4 GHz) / your Wi-Fi name (2.4 GHz only)
9const char* WIFI_PASS = "Sifre123"; // Wi-Fi şifren / your Wi-Fi password
10#define BOT_TOKEN "1234567890:AAExxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" // BotFather'ın verdiği token / token from BotFather
11#define CHAT_ID "123456789" // yalnız bu ID'ye yanıt verilir / only this ID gets answers
12
13// AI-Thinker ESP32-CAM kamera pinleri / AI-Thinker ESP32-CAM camera pins
14#define PWDN_GPIO_NUM 32
15#define RESET_GPIO_NUM -1
16#define XCLK_GPIO_NUM 0
17#define SIOD_GPIO_NUM 26
18#define SIOC_GPIO_NUM 27
19#define Y9_GPIO_NUM 35
20#define Y8_GPIO_NUM 34
21#define Y7_GPIO_NUM 39
22#define Y6_GPIO_NUM 36
23#define Y5_GPIO_NUM 21
24#define Y4_GPIO_NUM 19
25#define Y3_GPIO_NUM 18
26#define Y2_GPIO_NUM 5
27#define VSYNC_GPIO_NUM 25
28#define HREF_GPIO_NUM 23
29#define PCLK_GPIO_NUM 22
30
31#define FLAS_LED 4 // beyaz flaş LED'i / white flash LED
32#define PIR_PIN 13 // PIR OUT (SD kart kullanılmıyor) / PIR OUT (no SD card)
33
34const unsigned long PIR_ISINMA = 60000; // PIR ilk 60 sn dengelenir / the PIR settles in the first 60 s
35const unsigned long BEKLEME = 30000; // iki hareket fotoğrafı arası en az 30 sn / at least 30 s between motion photos
36const unsigned long KONTROL_ARALIGI = 1000; // Telegram'a saniyede bir sor / poll Telegram once per second
37
38WiFiClientSecure client;
39UniversalTelegramBot bot(BOT_TOKEN, client);
40
41camera_fb_t *fb = nullptr; // gönderilecek kare / the frame being sent
42bool veriBekliyor = false; // kütüphanenin geri çağırma bayrağı / flag for the library callbacks
43bool alarmKurulu = true; // hareket alarmı açık mı / is the motion alarm armed
44bool flasAcik = false; // fotoğrafta flaş kullanılsın mı / use the flash for photos
45bool pirOnceki = false;
46unsigned long sonFoto = 0;
47unsigned long sonKontrol = 0;
48
49bool kameraBaslat() {
50 camera_config_t config = {};
51 config.ledc_channel = LEDC_CHANNEL_0;
52 config.ledc_timer = LEDC_TIMER_0;
53 config.pin_d0 = Y2_GPIO_NUM;
54 config.pin_d1 = Y3_GPIO_NUM;
55 config.pin_d2 = Y4_GPIO_NUM;
56 config.pin_d3 = Y5_GPIO_NUM;
57 config.pin_d4 = Y6_GPIO_NUM;
58 config.pin_d5 = Y7_GPIO_NUM;
59 config.pin_d6 = Y8_GPIO_NUM;
60 config.pin_d7 = Y9_GPIO_NUM;
61 config.pin_xclk = XCLK_GPIO_NUM;
62 config.pin_pclk = PCLK_GPIO_NUM;
63 config.pin_vsync = VSYNC_GPIO_NUM;
64 config.pin_href = HREF_GPIO_NUM;
65 config.pin_sccb_sda = SIOD_GPIO_NUM;
66 config.pin_sccb_scl = SIOC_GPIO_NUM;
67 config.pin_pwdn = PWDN_GPIO_NUM;
68 config.pin_reset = RESET_GPIO_NUM;
69 config.xclk_freq_hz = 20000000; // 20 MHz kamera saati / 20 MHz camera clock
70 config.pixel_format = PIXFORMAT_JPEG;
71
72 if (psramFound()) {
73 config.frame_size = FRAMESIZE_XGA; // 1024x768: net ve hızlı gönderilir / sharp and quick to send
74 config.jpeg_quality = 12; // küçük = daha kaliteli / lower = better quality
75 config.fb_count = 2;
76 config.fb_location = CAMERA_FB_IN_PSRAM;
77 config.grab_mode = CAMERA_GRAB_LATEST;
78 } else {
79 config.frame_size = FRAMESIZE_SVGA; // PSRAM yoksa 800x600 / 800x600 without PSRAM
80 config.jpeg_quality = 12;
81 config.fb_count = 1;
82 config.fb_location = CAMERA_FB_IN_DRAM;
83 config.grab_mode = CAMERA_GRAB_WHEN_EMPTY;
84 }
85 return esp_camera_init(&config) == ESP_OK;
86}
87
88// sendPhotoByBinary için üç geri çağırma: kareyi tek parça verir
89// Three callbacks for sendPhotoByBinary: they hand over the frame in one piece
90bool veriVarMi() {
91 if (veriBekliyor) {
92 veriBekliyor = false;
93 return true;
94 }
95 return false;
96}
97byte *sonrakiTampon() { return fb ? fb->buf : nullptr; }
98int tamponBoyu() { return fb ? fb->len : 0; }
99
100String saat() { // "14:05:31" biçiminde saat / time as "14:05:31"
101 struct tm t;
102 if (!getLocalTime(&t, 100)) return "--:--:--";
103 char buf[9];
104 strftime(buf, sizeof(buf), "%H:%M:%S", &t);
105 return String(buf);
106}
107
108bool fotoGonder(const String& chatId, const String& yazi) {
109 if (flasAcik) {
110 digitalWrite(FLAS_LED, HIGH); // flaşı yak / flash on
111 delay(150); // pozlama yeni ışığa uysun / let exposure adapt
112 }
113 fb = esp_camera_fb_get(); // tampondaki eski kareyi at / discard the stale frame
114 if (fb) esp_camera_fb_return(fb);
115 fb = esp_camera_fb_get(); // asıl fotoğraf / the real photo
116 digitalWrite(FLAS_LED, LOW); // flaşı söndür / flash off
117
118 if (!fb) {
119 bot.sendMessage(chatId, "Kamera hatası / camera error");
120 return false;
121 }
122 bot.sendMessage(chatId, yazi); // önce kısa bildirim / a quick notification first
123 veriBekliyor = true;
124 String cevap = bot.sendPhotoByBinary(chatId, "image/jpeg", fb->len,
125 veriVarMi, nullptr, sonrakiTampon, tamponBoyu);
126 Serial.printf("Foto / photo: %u bayt / bytes\n", fb->len);
127 esp_camera_fb_return(fb); // kareyi sürücüye geri ver / give the frame back
128 fb = nullptr;
129 return cevap.indexOf("\"ok\":true") >= 0; // Telegram başarıyı böyle bildirir / how Telegram reports success
130}
131
132void mesajlariIsle(int adet) {
133 for (int i = 0; i < adet; i++) {
134 String chatId = bot.messages[i].chat_id;
135 String metin = bot.messages[i].text;
136 if (chatId != CHAT_ID) continue; // yabancılara yanıt yok / never answer strangers
137
138 int at = metin.indexOf('@'); // gruptaki "/foto@bot_adi" -> "/foto" / "/foto@bot_name" -> "/foto"
139 if (at > 0) metin = metin.substring(0, at);
140
141 if (metin == "/start") {
142 String yardim = "/foto - fotoğraf çek / take a photo\n";
143 yardim += "/flas - flaşı aç ya da kapat / toggle the flash\n";
144 yardim += "/alarm - hareket alarmını aç ya da kapat / arm or disarm\n";
145 yardim += "/durum - son durum / status";
146 bot.sendMessage(chatId, yardim);
147 } else if (metin == "/foto") {
148 if (!fotoGonder(chatId, "Fotoğraf / photo " + saat())) {
149 bot.sendMessage(chatId, "Gönderilemedi / send failed");
150 }
151 } else if (metin == "/flas") {
152 flasAcik = !flasAcik;
153 bot.sendMessage(chatId, flasAcik ? "Flaş AÇIK / flash ON" : "Flaş KAPALI / flash OFF");
154 } else if (metin == "/alarm") {
155 alarmKurulu = !alarmKurulu;
156 bot.sendMessage(chatId, alarmKurulu ? "Alarm KURULU / ARMED" : "Alarm KAPALI / DISARMED");
157 } else if (metin == "/durum") {
158 String s = "Alarm: " + String(alarmKurulu ? "KURULU / ARMED" : "KAPALI / DISARMED");
159 s += "\nFlaş / flash: " + String(flasAcik ? "AÇIK / ON" : "KAPALI / OFF");
160 s += "\nWi-Fi: " + String(WiFi.RSSI()) + " dBm";
161 bot.sendMessage(chatId, s);
162 }
163 }
164}
165
166void setup() {
167 Serial.begin(115200);
168 pinMode(FLAS_LED, OUTPUT);
169 digitalWrite(FLAS_LED, LOW);
170 pinMode(PIR_PIN, INPUT_PULLDOWN); // PIR bağlı değilken pin boşta kalmasın / keep the pin defined without a PIR
171
172 if (!kameraBaslat()) {
173 Serial.println("Kamera baslatilamadi / camera init failed");
174 delay(3000);
175 ESP.restart();
176 }
177
178 WiFi.mode(WIFI_STA);
179 WiFi.begin(WIFI_SSID, WIFI_PASS);
180 while (WiFi.status() != WL_CONNECTED) delay(500);
181 Serial.println(WiFi.localIP());
182
183 configTzTime("<+03>-3", "pool.ntp.org"); // Türkiye saati (UTC+3) / Turkey time (UTC+3)
184 client.setCACert(TELEGRAM_CERTIFICATE_ROOT); // api.telegram.org kök sertifikası / root certificate
185 bot.sendMessage(CHAT_ID, "Kamera hazır, /start yaz / camera ready, send /start");
186}
187
188void loop() {
189 bool pir = digitalRead(PIR_PIN);
190 // Yalnız yükselen kenarda, ısınmadan sonra ve bekleme süresi dolunca
191 // Only on a rising edge, after warm-up and once the cooldown has passed
192 if (pir && !pirOnceki && alarmKurulu && millis() > PIR_ISINMA && millis() - sonFoto > BEKLEME) {
193 sonFoto = millis();
194 fotoGonder(CHAT_ID, "Hareket algılandı! / Motion detected! " + saat());
195 }
196 pirOnceki = pir;
197
198 if (millis() - sonKontrol > KONTROL_ARALIGI) {
199 int adet = bot.getUpdates(bot.last_message_received + 1);
200 while (adet) {
201 mesajlariIsle(adet);
202 adet = bot.getUpdates(bot.last_message_received + 1);
203 }
204 sonKontrol = millis();
205 }
206}| Partie | Ce qu'elle fait |
|---|---|
| kameraBaslat() | Démarre la caméra en XGA (1024×768) avec deux tampons s'il y a de la PSRAM, sinon en SVGA (800×600) |
| fotoGonder() | Allume le flash si demandé, jette l'image périmée, en capture une nouvelle, envoie d'abord un court message puis la photo |
| mesajlariIsle() | Ne répond qu'aux commandes /start, /foto, /flas, /alarm et /durum venant de CHAT_ID |
| Partie PIR de loop() | Vérifie le front montant, les 60 s de chauffe et les 30 s d'attente |
| Partie Telegram de loop() | Demande les nouveaux messages une fois par seconde et traite dans l'ordre ceux qui se sont accumulés |
Le code surveille le front montant du PIR et non son niveau : même si le capteur reste à HIGH pendant 3 secondes, une seule photo est prise. L'attente de 30 secondes évite qu'une personne qui circule dans la pièce inonde ton téléphone de dizaines de photos par minute et que Telegram ralentisse plus d'un message par seconde vers la même conversation. Comme chaque interrogation est une requête HTTPS, l'appel à getUpdates peut bloquer la boucle environ une demi-seconde ; la sortie du HC-SR501 restant à HIGH au moins 3 secondes, aucun mouvement n'est manqué entre-temps. Carte toujours allumée, il s'écoule en général 2 à 4 secondes entre le mouvement et l'arrivée de la photo sur ton téléphone.
Contrôle du flash : quand l'allumer ?
La LED blanche à l'avant est commandée par un transistor relié à GPIO 4 et tire plus de 100 mA sur l'alimentation à pleine luminosité. Le code n'allume le flash qu'au moment de la photo et attend environ 150 ms ; comme l'exposition automatique de la caméra a besoin de quelques images pour s'adapter à la nouvelle lumière, la première image du tampon est elle aussi jetée. Le flash porte jusqu'à 1 à 2 mètres ; une porte ou un jardin plus loin ne sera pas éclairé. Si tu photographies à travers une vitre, éteins le flash, sinon tu ne verras que son reflet éclatant sur le verre. Pour un flash plus doux, tu peux appliquer une PWM sur GPIO 4 ; le curseur LED Intensity de l'exemple CameraWebServer fait exactement cela. N'oublie pas qu'un flash blanc qui se déclenche la nuit révèle aussi l'emplacement de la caméra ; pour une surveillance discrète, la partie IR plus bas convient mieux.
Économiser la batterie avec le deep sleep
Une ESP32-CAM avec le Wi-Fi actif consomme 150 à 200 mA en permanence, ce qui vide un accu 18650 de 2600 mAh en une demi-journée. Sur batterie, la carte doit passer presque tout son temps en deep sleep et ne se réveiller que lorsque le PIR se déclenche. Dans le code ci-dessous, GPIO 13 est la source de réveil ext0 : quand la sortie du PIR passe à HIGH, la carte redémarre de zéro, prend d'abord la photo, puis se connecte au Wi-Fi, envoie la photo et se rendort. L'ordre compte : la connexion Wi-Fi prend quelques secondes, et prendre la photo avant de se connecter capture l'instant exact du mouvement.
1#include <WiFi.h>
2#include <WiFiClientSecure.h>
3#include <UniversalTelegramBot.h>
4#include <ArduinoJson.h>
5#include "esp_camera.h"
6#include "driver/rtc_io.h"
7
8const char* WIFI_SSID = "AgAdi";
9const char* WIFI_PASS = "Sifre123";
10#define BOT_TOKEN "1234567890:AAExxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
11#define CHAT_ID "123456789"
12
13// AI-Thinker ESP32-CAM kamera pinleri / AI-Thinker ESP32-CAM camera pins
14#define PWDN_GPIO_NUM 32
15#define RESET_GPIO_NUM -1
16#define XCLK_GPIO_NUM 0
17#define SIOD_GPIO_NUM 26
18#define SIOC_GPIO_NUM 27
19#define Y9_GPIO_NUM 35
20#define Y8_GPIO_NUM 34
21#define Y7_GPIO_NUM 39
22#define Y6_GPIO_NUM 36
23#define Y5_GPIO_NUM 21
24#define Y4_GPIO_NUM 19
25#define Y3_GPIO_NUM 18
26#define Y2_GPIO_NUM 5
27#define VSYNC_GPIO_NUM 25
28#define HREF_GPIO_NUM 23
29#define PCLK_GPIO_NUM 22
30
31#define FLAS_LED 4 // flaş LED'i (RTC pini) / flash LED (RTC pin)
32#define PIR_PIN 13 // PIR OUT, ext0 ile uyandırır / PIR OUT, wakes via ext0
33
34RTC_DATA_ATTR int hareketSayisi = 0; // uykuda korunur / survives deep sleep
35
36WiFiClientSecure client;
37UniversalTelegramBot bot(BOT_TOKEN, client);
38camera_fb_t *fb = nullptr;
39bool veriBekliyor = false;
40
41bool veriVarMi() {
42 if (veriBekliyor) {
43 veriBekliyor = false;
44 return true;
45 }
46 return false;
47}
48byte *sonrakiTampon() { return fb ? fb->buf : nullptr; }
49int tamponBoyu() { return fb ? fb->len : 0; }
50
51bool kameraBaslat() {
52 camera_config_t config = {};
53 config.ledc_channel = LEDC_CHANNEL_0;
54 config.ledc_timer = LEDC_TIMER_0;
55 config.pin_d0 = Y2_GPIO_NUM;
56 config.pin_d1 = Y3_GPIO_NUM;
57 config.pin_d2 = Y4_GPIO_NUM;
58 config.pin_d3 = Y5_GPIO_NUM;
59 config.pin_d4 = Y6_GPIO_NUM;
60 config.pin_d5 = Y7_GPIO_NUM;
61 config.pin_d6 = Y8_GPIO_NUM;
62 config.pin_d7 = Y9_GPIO_NUM;
63 config.pin_xclk = XCLK_GPIO_NUM;
64 config.pin_pclk = PCLK_GPIO_NUM;
65 config.pin_vsync = VSYNC_GPIO_NUM;
66 config.pin_href = HREF_GPIO_NUM;
67 config.pin_sccb_sda = SIOD_GPIO_NUM;
68 config.pin_sccb_scl = SIOC_GPIO_NUM;
69 config.pin_pwdn = PWDN_GPIO_NUM;
70 config.pin_reset = RESET_GPIO_NUM;
71 config.xclk_freq_hz = 20000000;
72 config.pixel_format = PIXFORMAT_JPEG;
73 config.frame_size = psramFound() ? FRAMESIZE_XGA : FRAMESIZE_SVGA;
74 config.jpeg_quality = 12;
75 config.fb_count = 1; // tek kare yeter / one frame is enough
76 config.fb_location = psramFound() ? CAMERA_FB_IN_PSRAM : CAMERA_FB_IN_DRAM;
77 config.grab_mode = CAMERA_GRAB_WHEN_EMPTY;
78 return esp_camera_init(&config) == ESP_OK;
79}
80
81void uyu() {
82 // PIR çıkışı LOW'a dönmeden uyursan kart hemen yeniden uyanır
83 // If you sleep while the PIR output is still HIGH, the board wakes straight away
84 unsigned long t = millis();
85 while (digitalRead(PIR_PIN) == HIGH && millis() - t < 30000) delay(100);
86
87 digitalWrite(FLAS_LED, LOW);
88 rtc_gpio_hold_en(GPIO_NUM_4); // flaş uykuda sönük kalsın / keep the flash off while asleep
89 esp_sleep_enable_ext0_wakeup(GPIO_NUM_13, 1); // GPIO 13 HIGH olunca uyan / wake when GPIO 13 goes HIGH
90 Serial.println("Uyku / sleep");
91 Serial.flush();
92 esp_deep_sleep_start();
93}
94
95void setup() {
96 Serial.begin(115200);
97 rtc_gpio_hold_dis(GPIO_NUM_4); // flaş pinini serbest bırak / release the flash pin
98 rtc_gpio_deinit(GPIO_NUM_13); // pini yeniden normal GPIO yap / make the pin a normal GPIO again
99 pinMode(FLAS_LED, OUTPUT);
100 digitalWrite(FLAS_LED, LOW);
101 pinMode(PIR_PIN, INPUT);
102
103 if (esp_sleep_get_wakeup_cause() != ESP_SLEEP_WAKEUP_EXT0) {
104 Serial.println("Ilk acilis: PIR 60 sn isiniyor / first boot: PIR warming up 60 s");
105 delay(60000);
106 uyu();
107 }
108 hareketSayisi++;
109
110 // 1) Fotoğrafı hemen çek, Wi-Fi'yi sonra aç: an kaçmasın
111 // 1) Take the photo at once and start Wi-Fi later, so the moment is not lost
112 if (kameraBaslat()) {
113 for (int i = 0; i < 3; i++) { // ilk kareler karanlık ya da yeşil olur / first frames are dark or green
114 fb = esp_camera_fb_get();
115 if (fb) esp_camera_fb_return(fb);
116 }
117 fb = esp_camera_fb_get();
118 }
119
120 // 2) Wi-Fi'ye bağlan, en fazla 15 sn bekle / connect to Wi-Fi, wait at most 15 s
121 WiFi.mode(WIFI_STA);
122 WiFi.begin(WIFI_SSID, WIFI_PASS);
123 unsigned long t0 = millis();
124 while (WiFi.status() != WL_CONNECTED && millis() - t0 < 15000) delay(100);
125
126 // 3) Gönder ve uyu / send and go back to sleep
127 if (WiFi.status() == WL_CONNECTED) {
128 client.setCACert(TELEGRAM_CERTIFICATE_ROOT);
129 bot.sendMessage(CHAT_ID, "Hareket / motion #" + String(hareketSayisi));
130 if (fb) {
131 veriBekliyor = true;
132 bot.sendPhotoByBinary(CHAT_ID, "image/jpeg", fb->len,
133 veriVarMi, nullptr, sonrakiTampon, tamponBoyu);
134 } else {
135 bot.sendMessage(CHAT_ID, "Kamera hatası / camera error");
136 }
137 }
138 if (fb) esp_camera_fb_return(fb);
139 Serial.printf("Uyanik kalma / awake: %lu ms\n", millis());
140 uyu();
141}
142
143void loop() {
144}Quatre détails du code passent facilement inaperçus. Premièrement, la carte attend que la sortie du PIR revienne à LOW avant de dormir ; si elle s'endort alors que le signal est encore à HIGH, elle se réveille aussitôt et tourne en boucle. Deuxièmement, rtc_gpio_hold_en() bloque la broche du flash à LOW pendant le sommeil ; sans cette ligne, GPIO 4 reste flottante et le flash luit faiblement pendant le sommeil, ce qui vide la batterie et attire l'attention. Troisièmement, au réveil, rtc_gpio_deinit() remet GPIO 13 en entrée normale, sinon digitalRead() risque de mal la lire. Quatrièmement, le compteur marqué RTC_DATA_ATTR est conservé pendant le sommeil, si bien que chaque message t'indique le numéro du mouvement. Tous les modes de sommeil et sources de réveil sont présentés dans Deep sleep de l'ESP32.
| Scénario | Courant moyen (côté batterie) | Avec un 18650 de 2600 mAh |
|---|---|---|
| Toujours allumée, Wi-Fi connecté (premier code) | ≈ 290 mA | ≈ 9 heures |
| Deep sleep + 20 mouvements par jour | ≈ 11 mA | ≈ 10 jours |
| Circuit d'auto-maintien (carte totalement éteinte) + 20 mouvements par jour | ≈ 0,7 mA | ≈ 4 à 5 mois (théorique) |
Les valeurs du tableau supposent un convertisseur élévateur 5 V avec un rendement de 85 %. Même si la puce ESP32 elle-même ne consomme qu'environ 10 µA en deep sleep, la carte ESP32-CAM tire encore environ 6 mA en sommeil à cause de son régulateur, de la PSRAM et de la caméra, et c'est cela qui fixe vraiment l'autonomie. Chaque réveil dure 6 à 10 secondes : démarrage, caméra, connexion Wi-Fi et deux requêtes HTTPS. Pour des mois d'autonomie, il faut un circuit d'auto-maintien (power latch) qui coupe totalement l'alimentation de la carte avec un MOSFET, l'allume quand le PIR se déclenche et laisse la carte s'éteindre elle-même une fois le travail fini. Tu peux calculer ton propre scénario avec le Calculateur d'autonomie de batterie et, en cas de redémarrages fréquents, consulter Brownout de l'ESP32.
Photos de nuit : flash, LED IR et réglages de la caméra
Les objectifs standard de l'ESP32-CAM contiennent un filtre IR-cut fixe qui bloque la lumière infrarouge ; c'est pourquoi les couleurs paraissent naturelles de jour, mais une scène éclairée uniquement en IR reste sombre. Il y a deux façons de voir dans le noir : le flash blanc, ou un éclairage IR avec un objectif sans filtre. Les modules OV2640 vendus comme night vision ou 850 nm n'ont pas ce filtre ; de jour, les couleurs virent au rose, mais une scène éclairée par des LED IR de 850 nm apparaît nettement en noir et blanc. Les LED de 940 nm sont totalement invisibles, mais l'OV2640 est beaucoup moins sensible à cette longueur d'onde et l'image devient nettement plus sombre. Alimente le projecteur IR avec une source séparée de 5 V ou 12 V ; les modules tout faits équipés d'un capteur de lumière s'allument seuls à la tombée de la nuit. Pour le commander depuis un GPIO, utilise un MOSFET et ne branche jamais les LED directement sur la broche.
1#include "esp_camera.h"
2void setup() {
3// esp_camera_init() başarılı olduktan sonra / after esp_camera_init() succeeds
4sensor_t *s = esp_camera_sensor_get();
5s->set_gainceiling(s, GAINCEILING_32X); // karanlıkta daha yüksek kazanç / more gain in the dark
6s->set_aec2(s, 1); // DSP pozlaması: gece modu / DSP exposure: night mode
7s->set_ae_level(s, 2); // pozlamayı artır (-2..2) / raise exposure (-2..2)
8s->set_whitebal(s, 1); // otomatik beyaz dengesi / auto white balance
9s->set_special_effect(s, 2); // 2 = siyah-beyaz: IR'de renk zaten bozulur / 2 = grayscale: IR spoils colour anyway
10}
11void loop() {}Ces réglages relèvent le plafond de gain et le temps d'exposition du capteur. En contrepartie, la cadence d'images baisse dans le noir, une personne en mouvement apparaît floue et le gain élevé ajoute du grain. La lumière reste le moyen le plus efficace d'améliorer une photo de nuit ; même un petit projecteur IR donne un résultat plus net que d'augmenter le gain. Tu peux ajouter ce code juste après l'appel à kameraBaslat() et, si tu veux, mesurer la lumière ambiante avec une LDR pour passer automatiquement des réglages de jour à ceux de nuit.
Ses limites comme caméra de surveillance
- Pas d'enregistrement vidéo : Ce système n'envoie que des photos. Pour un enregistrement continu ou revoir ce qui s'est passé, il faut une vraie caméra de surveillance.
- Il y a un délai : 2 à 4 secondes carte allumée, 6 à 10 secondes au réveil du deep sleep ; une personne qui passe vite peut déjà être sortie du cadre.
- Le PIR ne voit pas à travers une vitre : Le verre ne laisse pas passer le rayonnement thermique infrarouge. Un capteur placé derrière une fenêtre ne détecte pas les mouvements dehors, mais le soleil et les radiateurs peuvent quand même le déclencher à tort.
- Il dépend d'internet : Si le Wi-Fi ou internet tombe, la photo ne part pas. Pour un usage critique, enregistrer aussi la photo sur une carte SD sert de sauvegarde.
- Aucune protection physique : La carte s'arrache ou s'éteint facilement et, dehors, elle souffre de l'humidité et du soleil ; en extérieur, il te faut un boîtier étanche et une alimentation qui supporte la température.
- Qualité d'image limitée : L'OV2640 de 2 Mpx ne suffit pas pour reconnaître un visage ou lire une plaque à quelques mètres ; les objectifs grand-angle élargissent le cadre mais réduisent les détails.
- Règles de confidentialité : Oriente la caméra vers ton propre espace ; avant de filmer le jardin du voisin, les parties communes ou la rue, vérifie les règles légales de ton lieu de résidence.
Idées de projets ESP32-CAM avec Telegram
- Caméra de sonnette : Branche le bouton de sonnette sur GPIO 13 pour qu'un appui photographie l'entrée et te l'envoie ; vois brancher un bouton poussoir pour le câblage.
- Alerte boîte aux lettres : Place un interrupteur reed sur le clapet pour qu'en l'ouvrant la carte se réveille et envoie une photo de l'intérieur.
- Caméra de nichoir ou d'animal : Prendre une photo dès qu'il y a du mouvement et vérifier dans la journée avec /foto.
- Des yeux pour une alarme : Fais en sorte qu'un système d'alarme Arduino prévienne l'ESP32-CAM quand il se déclenche pour qu'elle photographie les lieux.
- Suivi des plantes et de la serre : Réveil par minuterie toutes les heures pour envoyer une photo, et envoi des mesures vers un tableau de bord avec MQTT et Home Assistant.
- Groupe familial : Ajoute le bot à un groupe Telegram et utilise l'ID du groupe pour que la photo arrive chez tout le monde en même temps.
Erreurs fréquentes et solutions
| Symptôme | Cause probable | Solution |
|---|---|---|
| Le message arrive, mais pas la photo | Image trop lourde, PSRAM désactivée ou envoi interrompu | Choisis la carte AI Thinker ESP32-CAM, descends la résolution en SVGA, améliore le signal Wi-Fi |
| Le bot ne répond à aucune commande | Token ou CHAT_ID erroné, ou jamais envoyé /start au bot | Recopie le token, vérifie ton ID avec @userinfobot, envoie /start à ton bot |
| Des photos de mouvement arrivent sans raison | Le PIR capte le signal de l'antenne Wi-Fi ou une source de chaleur | Éloigne le PIR de 10 à 20 cm de la carte, ajoute 100 nF entre VCC et GND, ne vise ni le soleil ni les radiateurs |
| La carte se réveille juste après s'être endormie | La sortie du PIR est encore à HIGH au moment du sommeil | Attends que la broche repasse à LOW avant de dormir, règle le potentiomètre de durée du HC-SR501 au minimum |
| Le flash luit faiblement pendant le sommeil | GPIO 4 reste flottante | Écris LOW et appelle rtc_gpio_hold_en(GPIO_NUM_4) avant de dormir |
| La photo est sombre ou verdâtre | Les premières images juste après le démarrage de la caméra | Capture et jette 2 à 3 images avant la vraie photo |
| La carte redémarre pendant l'envoi d'une photo | Brownout : l'alimentation ne suit pas le courant du Wi-Fi et du flash | Utilise un adaptateur 5 V / 2 A, des fils courts et un condensateur de 470 à 1000 µF entre 5V et GND |
Associée à Telegram, l'ESP32-CAM transforme une carte bon marché en un œil intelligent qui envoie des photos sur ton téléphone. Monte d'abord le système avec le code toujours allumé et règle le PIR, puis passe à la version deep sleep si tu en as besoin. Dans l'application BlueGrays, tu trouveras l'alarme de mouvement Telegram avec PIR, buzzer et LED, le deep sleep de l'ESP32 et l'éclairage automatique avec PIR, avec schémas de câblage et code prêt à l'emploi, étape par étape.
Questions fréquentes
Comment envoyer une photo de l'ESP32-CAM sur Telegram ?
Tu crées un bot avec BotFather pour obtenir un token, tu installes la bibliothèque UniversalTelegramBot et tu envoies le tampon d'image de la caméra à la méthode sendPhoto de Telegram avec la fonction sendPhotoByBinary(). La bibliothèque transmet la photo en HTTPS au format multipart, comme un envoi de fichier dans un navigateur. Dans le code, il suffit de changer les identifiants Wi-Fi, le token et ton chat ID.
Sur quelle broche de l'ESP32-CAM brancher un capteur PIR ?
Sans carte SD, GPIO 13 est le choix le plus sûr : elle n'influence pas le démarrage et, en tant que broche RTC, elle peut réveiller la carte du deep sleep. Relie le VCC du PIR à la broche 5V, le GND à GND et OUT à GPIO 13. Évite GPIO 12 ; si elle est à HIGH au démarrage, la carte ne démarre pas.
Combien de temps une ESP32-CAM tient-elle sur batterie ?
Avec le Wi-Fi allumé en permanence, un accu 18650 de 2600 mAh tient environ 9 heures. Avec le deep sleep et un réveil uniquement sur mouvement, on monte à une dizaine de jours ; même en sommeil, la carte consomme environ 6 mA à cause du régulateur et de la PSRAM. Pour des mois d'autonomie, il faut un circuit d'auto-maintien qui coupe complètement l'alimentation.
L'ESP32-CAM peut-elle voir la nuit ?
Le filtre IR-cut des modules standard bloque la lumière infrarouge, donc dans le noir tu n'obtiens une image qu'à 1 ou 2 mètres avec le flash blanc. Avec un module OV2640 sans filtre (night vision) et un projecteur IR de 850 nm, tu peux prendre des photos nettes en noir et blanc dans l'obscurité. Relever le plafond de gain dans les réglages aide aussi, mais ajoute du grain.
L'ESP32-CAM peut-elle envoyer une vidéo sur Telegram ?
En pratique, non. L'ESP32-CAM produit des images JPEG séparées et n'a ni la puissance ni la mémoire pour en faire une vidéo MP4. Envoyer plusieurs photos à la suite ou regarder le flux en direct sur ton réseau avec CameraWebServer sont des solutions plus réalistes.
Peut-on envoyer la photo à plusieurs personnes ?
Oui. Si tu ajoutes le bot à un groupe Telegram et que tu utilises comme CHAT_ID l'ID du groupe, qui commence par un signe moins, la photo arrive chez tous les membres en même temps. Tu peux aussi mettre plusieurs chat ID dans un tableau et envoyer la photo à chacun tour à tour ; chaque envoi est un téléversement séparé, donc c'est plus long.
Projets à réaliser après cet article
Voir tous les projets
Système d'Alarme de Mouvement ESP32 (PIR + Buzzer + LED + Telegram)
Un système d'alarme de sécurité intelligent qui active le buzzer et la LED lorsque le capteur PIR détecte un mouvement, tout en envoyant une notification instantanée au propriétaire via Telegram. Il intègre un délai d'attente de 30 secondes pour limiter la fréquence des alarmes répétées ; ce mécanisme ne vérifie pas la validité des détections du capteur PIR et n'élimine pas les faux déclenchements. Idéal pour la sécurité domestique, la surveillance de garage, d'entrepôts ou de bureaux. Avec l'ES

Éclairage automatique avec le capteur de mouvement PIR ESP32
Dans ce projet, nous allons détecter toute personne entrant dans la pièce à l'aide du capteur de mouvement PIR HC-SR501 et allumer automatiquement la LED. Nous suivrons la durée d'allumage de la LED avec millis() ; chaque lecture HIGH du capteur actualisera l'instant du dernier mouvement, et la LED s'éteindra au moins 10 secondes après la dernière lecture HIGH. Le code actuel attend également 50 ms à chaque itération avec delay(50) ; la boucle de l'application n'est donc pas entièrement non

Mode veille profonde de l'ESP32 — Économie de batterie (OLED + compteur RAM RTC)
Le mode « Deep Sleep » de l'ESP32 réduit la consommation de courant à environ 10 µA en désactivant le processeur et la plupart des périphériques, ce qui revêt une importance cruciale pour les appareils IoT fonctionnant sur batterie. Dans ce projet, nous utilisons trois sources de réveil différentes : une minuterie (timer, 15 s), une broche externe (EXT0 — bouton GPIO 33) et un capteur tactile (T0/GPIO 4). Les variables marquées avec RTC_DATA_ATTR sont conservées pendant le mode Deep Sleep — le

Éclairage Intelligent ESP32 (PIR + LDR + Relais + OLED)
Transformez votre couloir, votre salle de bain ou votre garde-manger en éclairage intelligent avec l'ESP32 ! En utilisant un capteur de mouvement PIR, un capteur de lumière LDR et un module relais, nous construisons le scénario classique "la lumière s'allume lorsqu'un mouvement est détecté dans l'obscurité" — si l'environnement est déjà lumineux, le mouvement N'ACTIVE PAS la lumière, économisant ainsi de l'énergie pendant la journée. La lumière reste allumée pendant 30 secondes ; si le mouvement

Connexion Wi-Fi STA ESP32 (avec écran LCD)
Dans ce projet, nous connectons l'ESP32 au réseau Wi-Fi domestique en mode Station (STA) de manière sécurisée et tolérante aux pannes. Nous isolons SSID/mot de passe dans un fichier secrets.h pour empêcher leur fuite sur Git, mettons en place une reconnexion automatique, et affichons le niveau RSSI et l'adresse IP sur un écran LCD I2C. La LED intégrée sert d'indicateur d'état.

ESP32 mDNS : accès à l'appareil via bluegrays-esp.local (avec écran LCD)
Dans ce projet, en ajoutant un service mDNS (Multicast DNS) à l'ESP32, nous permettons d'accéder à l'appareil via un nom d'hôte lisible, tel que bluegrays-esp.local, plutôt qu'à l'aide d'une adresse IP. Même si l'adresse IP attribuée par le DHCP venait à changer, vous pourrez toujours accéder à l'appareil sous le même nom. En exécutant également un petit serveur web sur l'appareil, nous envoyons des commandes de contrôle des LED depuis un navigateur via l'adresse http://bluegrays-esp.local/.

Détecteur de mouvement avec capteur de mouvement
Un gardien tout simple qui te prévient quand quelqu'un entre dans la pièce. Le détecteur de mouvement repère la chaleur dégagée par le corps humain ; l'Arduino garde la LED allumée tant qu'il y a du m
Autres articles
Application BlueGrays
Réalise ce montage pas à pas sur ton téléphone
Schéma, code et liste des composants sur un seul écran. Plus de 200 projets Arduino, ESP32 et Pico à réaliser.
Scanne avec ton téléphone
Pointe l’appareil photo vers le code : la boutique d’applications de ton téléphone s’ouvre.
ou ouvre la boutique

