ESP32-CAM Telegram: Kirim Foto Begitu Gerakan Terdeteksi
Kirim foto dari ESP32-CAM ke Telegram: pengkabelan PIR dan pin yang masih bebas, perintah /foto, kendali flash, deep sleep untuk baterai, foto malam, dan kode lengkap.
Ketika kamu membuat bot ESP32-CAM Telegram, board-nya memotret begitu mendeteksi gerakan dan mengirim fotonya ke ponselmu dalam beberapa detik. Kamu tidak perlu menginstal aplikasi, menyewa server, atau membuka port di router-mu: board meng-upload foto langsung ke server Telegram, dan Telegram mengantarkannya kepadamu sebagai notifikasi instan. Lewat bot yang sama kamu bisa mengetik /foto untuk melihat depan pintumu kapan saja, menyalakan atau mematikan flash, dan menonaktifkan alarm.
Di panduan ini kamu akan mempelajari langkah demi langkah pin mana dari sedikit pin bebas ESP32-CAM yang dipakai untuk sensor PIR, bagaimana foto di-upload ke Telegram, kode lengkap yang dijalankan lewat perintah, dan cara memakai flash dengan benar. Setelah itu kamu akan menjalankan board dengan baterai selama berhari-hari berkat deep sleep, memotret di malam hari dengan lampu IR, dan melihat di mana sistem ini kurang memadai sebagai kamera keamanan. Kalau kamu belum menyiapkan board dan meng-upload sketch pertamamu, baca dulu Tutorial ESP32-CAM.

Bagaimana sistem foto ESP32-CAM Telegram bekerja?
Rantainya terdiri dari empat langkah. Sensor PIR mendeteksi perubahan panas yang dipancarkan tubuh manusia dan menarik pin OUT-nya ke 3,3 V. Begitu ESP32 melihat tepi naik ini, ia meminta satu frame JPEG dari kamera OV2640, yang lalu ditulis ke PSRAM 4 MB di board. Kemudian ESP32 membuka koneksi HTTPS terenkripsi dan mengirim request POST berformat multipart/form-data ke endpoint sendPhoto milik Telegram Bot API; caranya sama dengan yang dipakai browser ketika kamu meng-upload file. Telegram menambahkan foto itu ke chat-mu dan ponselmu menampilkan notifikasi. Perintah berjalan ke arah sebaliknya: setiap detik ESP32 memanggil getUpdates untuk menanyakan apakah ada pesan baru, dan kalau /foto sudah masuk, ia menjalankan proses upload yang sama untuk permintaanmu.
| Komponen | Spesifikasi | Catatan |
|---|---|---|
| ESP32-CAM (AI-Thinker) | OV2640, PSRAM 4 MB | Versi dengan PSRAM sanggup menangani foto XGA dengan mudah |
| ESP32-CAM-MB atau konverter USB-TTL | CH340 / CP2102 | Untuk meng-upload kode; board-nya tidak punya USB |
| Sensor PIR | HC-SR501 (5 V) atau AM312 (3,3 V) | Sinyal OUT-nya 3,3 V dan langsung tersambung ke ESP32 |
| Catu daya | 5 V, minimal 2 A | Wi-Fi dan flash menarik arus tinggi secara bersamaan |
| Kapasitor elektrolit | 470–1000 µF | Di antara 5V dan GND, dekat dengan board |
| Opsional | Sel 18650 + konverter boost 5 V, iluminator IR 850 nm | Untuk pemakaian dengan baterai dan foto malam |
Pengkabelan sensor PIR dan memilih pin yang bebas
ESP32-CAM punya 16 pin di tepinya, tapi kamera sendiri sudah memakai 15 GPIO dan slot microSD ikut memakai enam pin lagi. Sebagian besar sisanya adalah pin boot (strapping) atau dibutuhkan untuk port serial. Karena proyek ini tidak memakai kartu SD, jalur SD jadi bebas, dan yang paling aman di antaranya adalah GPIO 13: pin ini tidak memengaruhi proses boot, punya resistor pull-down internal, dan karena merupakan pin RTC, ia bisa membangunkan board dari deep sleep. Tabel di bawah juga membandingkan kandidat lainnya.
| Pin | Tanpa kartu SD | Bisa membangunkan dari deep sleep? | Catatan |
|---|---|---|---|
| GPIO 13 | Bebas | Ya (ext0) | Pilihan terbaik untuk PIR |
| GPIO 14 | Bebas | Ya | Bisa memunculkan sinyal PWM singkat ketika boot; aman sebagai input |
| GPIO 15 | Bebas | Ya | Pin strapping; kalau ditahan LOW, pesan boot jadi tidak muncul |
| GPIO 2 | Bebas | Ya | Pin strapping; tidak boleh HIGH selama upload |
| GPIO 12 | Bebas | Ya | Kalau HIGH ketika boot, board tidak mau menyala; jangan dipakai |
| GPIO 4 | LED flash | Ya | Khusus untuk flash; jangan dipakai untuk hal lain |
| GPIO 3 (U0R) | RX serial | Tidak | Hanya kalau kamu tidak butuh port serial |
| GPIO 16 | PSRAM | Tidak | Jangan pernah dipakai |
| Pin PIR | Pin ESP32-CAM | Keterangan |
|---|---|---|
| VCC | 5V | HC-SR501 butuh minimal 4,5 V; pakai 3V3 untuk AM312 |
| OUT | GPIO 13 | 3,3 V ketika ada gerakan, 0 V ketika tidak ada |
| GND | GND | Ground bersama |
Dari dua trimpot di HC-SR501, putar trimpot waktu (Tx) ke pengaturan terpendek (putar penuh berlawanan arah jarum jam, sekitar 3 detik); waktu tunggunya akan diatur oleh kode. Biarkan jumper pemicu di posisi H (pemicu berulang) dan atur sensitivitas di mana saja antara 3 dan 7 meter sesuai kebutuhan. Setelah dinyalakan, sensor butuh sekitar satu menit untuk menyesuaikan diri dengan lingkungannya dan terpicu secara acak selama waktu itu, jadi kode mengabaikan 60 detik pertama. Detail cara kerja sensor ini ada di sensor gerak PIR.
Menyiapkan bot Telegram: token dan chat ID
- Buka @BotFather di Telegram, ketik /newbot, lalu beri bot-mu nama dan username yang diakhiri dengan bot.
- Salin token dari BotFather, yang bentuknya seperti 1234567890:AAE...; token ini masuk ke baris BOT_TOKEN di kode.
- Untuk mengetahui nomor penggunamu sendiri, kirim /start ke @userinfobot; nilai Id yang dibalasnya adalah CHAT_ID-mu.
- Buka bot-mu sendiri dan kirim /start sekali; Telegram tidak mengizinkan bot mengirim pesan ke orang yang belum pernah mengobrol dengannya.
- Di Library Manager Arduino IDE, instal library UniversalTelegramBot (Brian Lough) dan ArduinoJson.
- Pilih AI Thinker ESP32-CAM di Tools > Board dan pastikan Partition Scheme-nya Huge APP (3MB No OTA/1MB SPIFFS).
Mengirim foto ke Telegram: bagaimana sendPhotoByBinary bekerja?
Fungsi sendPhotoByBinary() di library UniversalTelegramBot meng-upload foto persis seperti sebuah file. Fungsi ini lebih dulu mengirim header request dan field chat ID, lalu meminta datanya dari tiga fungsi kecil yang kamu sediakan: veriVarMi() (bahasa Turki untuk “ada data?”) memberi tahu apakah masih ada potongan yang harus dikirim, sonrakiTampon() (“buffer berikutnya”) memberikan alamat data di memori, dan tamponBoyu() (“ukuran buffer”) menyebutkan berapa byte ukurannya. Karena frame buffer kamera sudah berupa satu JPEG utuh, kode menyerahkan seluruh foto sekaligus lalu menurunkan flag-nya. Foto 1024×768 dengan kualitas 12 berukuran 50–120 KB tergantung adegannya dan ter-upload dalam 1–2 detik lewat koneksi Wi-Fi yang bagus. Telegram menerima foto hingga 10 MB, jadi kamu tidak akan pernah mendekati batas ukurannya; batas yang sebenarnya adalah waktu upload.
Kode lengkap: perintah /foto, kendali flash, dan alarm gerakan
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}| Bagian | Fungsinya apa? |
|---|---|
| kameraBaslat() (“mulai kamera”) | Memulai kamera di XGA (1024×768) dengan dua frame buffer kalau ada PSRAM, kalau tidak di SVGA (800×600) |
| fotoGonder() (“kirim foto”) | Menyalakan flash kalau diminta, membuang frame lama dan mengambil yang baru, mengirim pesan singkat lebih dulu lalu fotonya |
| mesajlariIsle() (“proses pesan”) | Hanya menjawab perintah /start, /foto, /flas (bahasa Turki untuk “flash”), /alarm, dan /durum (“status”) yang datang dari CHAT_ID |
| Bagian PIR di loop() | Memeriksa tepi naik, pemanasan 60 dtk, dan jeda (cooldown) 30 dtk |
| Bagian Telegram di loop() | Menanyakan pesan baru sekali per detik dan memproses pesan yang mengantre satu per satu |
Kode ini memantau tepi naik PIR, bukan level-nya: meskipun sensor tetap HIGH selama 3 detik, hanya satu foto yang diambil. Jeda 30 detik mencegah orang yang mondar-mandir di ruangan membanjiri ponselmu dengan puluhan foto per menit, dan menjaga agar Telegram tidak membatasi lebih dari satu pesan per detik ke chat yang sama. Karena setiap pengambilan pesan adalah request HTTPS, panggilan getUpdates bisa menahan loop sekitar setengah detik; karena output HC-SR501 tetap HIGH minimal 3 detik, tidak ada gerakan yang terlewat selama itu. Dengan board yang selalu menyala, biasanya ada jeda 2–4 detik antara gerakan dan foto yang tiba di ponselmu.
Kendali flash: kapan sebaiknya dipakai?
LED putih di bagian depan dikendalikan lewat transistor yang tersambung ke GPIO 4 dan menarik lebih dari 100 mA dari catu daya pada kecerahan penuh. Kode menyalakan flash hanya pada momen pemotretan dan menunggu sekitar 150 ms; karena eksposur otomatis kamera butuh beberapa frame untuk menyesuaikan diri dengan cahaya baru, frame pertama di buffer juga dibuang. Flash efektif hingga 1–2 meter; pintu atau kebun yang lebih jauh tidak akan terang. Kalau kamu memotret dari balik kaca, matikan flash, kalau tidak yang terlihat di foto hanya pantulan terang di kaca. Kalau ingin flash yang lebih redup, kamu bisa menerapkan PWM ke GPIO 4; slider LED Intensity di contoh CameraWebServer melakukan persis hal itu. Ingat bahwa flash putih yang menyala di malam hari juga membocorkan letak kamera; untuk pemantauan diam-diam, bagian IR di bawah lebih cocok.
Menghemat baterai dengan deep sleep
ESP32-CAM dengan Wi-Fi menyala terus-menerus menarik 150–200 mA, yang menghabiskan sel 18650 2600 mAh dalam setengah hari. Kalau berjalan dengan baterai, board harus menghabiskan hampir seluruh waktunya dalam deep sleep dan hanya bangun ketika PIR terpicu. Di kode di bawah, GPIO 13 adalah sumber bangun ext0: ketika output PIR menjadi HIGH, board boot dari awal, memotret lebih dulu, lalu terhubung ke Wi-Fi, mengirim foto, dan kembali tidur. Urutannya penting: koneksi Wi-Fi butuh beberapa detik, dan memotret sebelum terhubung menangkap momen gerakan yang sebenarnya.
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}Ada empat detail di kode yang mudah terlewat. Pertama, board menunggu output PIR kembali ke LOW sebelum tidur; kalau kamu menidurkannya ketika sinyal masih HIGH, board langsung bangun lagi dan terjebak dalam perulangan. Kedua, rtc_gpio_hold_en() mengunci pin flash di LOW selama tidur; tanpa baris ini GPIO 4 mengambang dan flash menyala redup selama tidur, menguras baterai dan menarik perhatian. Ketiga, setelah bangun, rtc_gpio_deinit() mengembalikan GPIO 13 menjadi input biasa, kalau tidak digitalRead() bisa salah membacanya. Keempat, penghitung yang ditandai dengan RTC_DATA_ATTR tetap bertahan selama tidur, jadi setiap pesan memberi tahu gerakan ke berapa yang sedang dilaporkan. Semua mode sleep dan sumber bangun dibahas di deep sleep ESP32.
| Skenario | Arus rata-rata (sisi baterai) | Dengan 18650 2600 mAh |
|---|---|---|
| Selalu menyala, Wi-Fi terhubung (sketch pertama) | ≈ 290 mA | ≈ 9 jam |
| Deep sleep + 20 gerakan per hari | ≈ 11 mA | ≈ 10 hari |
| Rangkaian power latch (board mati total) + 20 gerakan per hari | ≈ 0,7 mA | ≈ 4–5 bulan (teoretis) |
Nilai di tabel menganggap konverter boost 5 V bekerja dengan efisiensi 85%. Meskipun chip ESP32 sendiri hanya memakai sekitar 10 µA dalam deep sleep, board ESP32-CAM tetap menarik sekitar 6 mA selama tidur karena regulator, PSRAM, dan kameranya, dan angka inilah yang benar-benar menentukan umur baterai. Setiap kali bangun butuh 6–10 detik: boot, kamera, koneksi Wi-Fi, dan dua request HTTPS. Kalau ingin berjalan berbulan-bulan, kamu butuh rangkaian power latch (pengunci daya) yang memutus catu daya board sepenuhnya dengan MOSFET, menyalakan board ketika PIR terpicu, dan membiarkan board mematikan dirinya sendiri setelah selesai. Kamu bisa menghitung skenariomu sendiri dengan Kalkulator umur baterai, dan untuk masalah restart yang sering, lihat brownout ESP32.
Foto malam: flash, LED IR, dan pengaturan kamera
Lensa ESP32-CAM standar punya filter IR-cut permanen yang menghalangi cahaya inframerah; karena itu warna terlihat alami di siang hari, tapi adegan yang hanya diterangi IR tetap gelap. Ada dua cara untuk melihat dalam gelap: flash putih, atau lampu IR dengan lensa tanpa filter. Modul OV2640 yang dijual sebagai night vision atau 850 nm tidak punya filter ini; warnanya jadi kemerahmudaan di siang hari, tapi adegan yang diterangi LED IR 850 nm akan terlihat jelas dalam hitam-putih. LED 940 nm sama sekali tidak terlihat mata, tapi OV2640 jauh kurang sensitif di panjang gelombang itu, sehingga gambarnya jadi jauh lebih gelap. Beri daya iluminator IR dari catu daya 5 V atau 12 V terpisah; modul siap pakai yang punya sensor cahaya akan menyala sendiri ketika hari mulai gelap. Kalau ingin mengendalikannya dari GPIO, pakai MOSFET dan jangan pernah menyambungkan LED langsung ke pin.
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() {}Pengaturan ini menaikkan batas atas gain (penguatan) sensor dan waktu eksposurnya. Harganya, frame rate turun dalam gelap, orang yang bergerak jadi buram, dan gain yang tinggi menambahkan bintik (noise) ke gambar. Cahaya tetap cara paling efektif untuk memperbaiki foto malam; iluminator IR yang lemah pun memberi hasil lebih jelas daripada menaikkan gain. Kamu bisa menambahkan kode ini tepat setelah pemanggilan kameraBaslat(), dan kalau mau, ukur cahaya sekitar dengan LDR lalu beralih otomatis antara pengaturan siang dan malam.
Batasannya sebagai kamera keamanan
- Tidak merekam video: Sistem ini hanya mengirim foto diam. Untuk perekaman terus-menerus atau melihat ulang rekaman, kamu butuh kamera keamanan sungguhan.
- Ada jeda: 2–4 detik kalau board menyala, 6–10 detik kalau bangun dari deep sleep; orang yang lewat dengan cepat mungkin sudah keluar dari bingkai foto.
- PIR tidak bisa melihat menembus kaca: Kaca tidak meneruskan radiasi panas inframerah. Sensor yang diletakkan di balik jendela tidak akan mendeteksi gerakan di luar, tapi sinar matahari dan pemanas ruangan tetap bisa memicunya secara keliru.
- Bergantung pada internet: Kalau Wi-Fi atau internet putus, foto tidak terkirim. Untuk pemakaian yang penting, menyimpan foto juga ke kartu SD memberimu cadangan.
- Tidak punya perlindungan fisik: Board mudah dicopot atau dimatikan, dan di luar ruangan terpengaruh kelembapan dan sinar matahari; kalau dipakai di luar, kamu butuh kotak tertutup rapat dan catu daya yang tahan terhadap suhu.
- Kualitas gambarnya terbatas: OV2640 2 MP tidak cukup bagus untuk mengenali wajah atau membaca pelat nomor dari jarak beberapa meter; lensa sudut lebar memperluas bingkai tapi mengurangi detail.
- Aturan privasi: Arahkan kamera ke propertimu sendiri; sebelum merekam kebun tetangga, area bersama, atau jalan, cek aturan hukum di tempat tinggalmu.
Ide proyek ESP32-CAM Telegram
- Kamera bel pintu: Sambungkan tombol bel pintu ke GPIO 13 sehingga setiap kali ditekan, board memotret depan pintumu dan mengirimkannya kepadamu; untuk pengkabelannya, lihat cara menghubungkan tombol.
- Notifikasi kotak surat: Pasang reed switch di tutupnya sehingga begitu dibuka, board bangun dan mengirim foto isinya.
- Kamera rumah burung atau hewan peliharaan: Ambil foto ketika ada gerakan dan intip kapan saja di siang hari dengan /foto.
- Mata untuk sistem alarm: Buat sistem alarm Arduino mengirim sinyal ke ESP32-CAM ketika terpicu sehingga ESP32-CAM memotret lokasi kejadian.
- Pemantauan tanaman dan rumah kaca: Bangunkan board dengan timer sekali setiap jam untuk mengirim foto, dan kirim hasil pengukuranmu ke dashboard lewat MQTT dan Home Assistant.
- Grup keluarga: Tambahkan bot ke grup Telegram dan pakai ID grupnya, sehingga foto sampai ke semua orang sekaligus.
Masalah umum dan solusinya
| Gejala | Kemungkinan penyebab | Solusi |
|---|---|---|
| Pesan masuk tapi fotonya tidak | Frame terlalu besar, PSRAM mati, atau upload terputus di tengah jalan | Pilih board AI Thinker ESP32-CAM, turunkan resolusi ke SVGA, perkuat sinyal Wi-Fi |
| Bot tidak menjawab perintah apa pun | Token atau CHAT_ID salah, atau kamu belum pernah mengirim /start ke bot | Salin ulang token-nya, cek ID-mu dengan @userinfobot, kirim /start ke bot-mu |
| Foto gerakan datang tanpa alasan | PIR terganggu sinyal dari antena Wi-Fi atau oleh sumber panas | Jauhkan PIR 10–20 cm dari board, tambahkan 100 nF di antara VCC-GND, jangan arahkan sensor ke matahari atau pemanas ruangan |
| Board langsung bangun lagi setelah tidur | Output PIR masih HIGH ketika board tidur | Tunggu pin kembali ke LOW sebelum tidur, atur trimpot waktu HC-SR501 ke minimum |
| Flash menyala redup selama tidur | GPIO 4 dibiarkan mengambang | Tulis LOW dan panggil rtc_gpio_hold_en(GPIO_NUM_4) sebelum tidur |
| Foto gelap atau kehijauan | Frame-frame pertama tepat setelah kamera menyala | Ambil dan buang 2–3 frame sebelum foto yang sebenarnya |
| Board restart ketika mengirim foto | Brownout: catu daya tidak sanggup mengimbangi arus Wi-Fi dan flash | Pakai adaptor 5 V / 2 A, kabel pendek, dan kapasitor 470–1000 µF di antara 5V-GND |
Digabungkan dengan Telegram, ESP32-CAM mengubah board murah menjadi mata pintar yang mengirim foto ke ponselmu. Bangun sistemnya dengan sketch yang selalu menyala lebih dulu dan setel PIR-nya, lalu pindah ke versi deep sleep kalau memang butuh. Di aplikasi BlueGrays kamu akan menemukan proyek “Sistem Alarm Gerakan ESP32 (PIR + Buzzer + LED + Telegram)”, “Deep Sleep ESP32: Hemat Baterai”, dan “Lampu Otomatis dengan Sensor Gerak PIR ESP32”, masing-masing dengan skema pengkabelan langkah demi langkah dan kode siap pakai.
Pertanyaan umum
Bagaimana cara mengirim foto dari ESP32-CAM ke Telegram?
Buat bot dengan BotFather untuk mendapatkan token, instal library UniversalTelegramBot, lalu upload frame buffer kamera ke endpoint sendPhoto Telegram dengan fungsi sendPhotoByBinary(). Library ini mengirim foto lewat HTTPS dalam format multipart, seperti upload file di browser. Di kode, kamu hanya perlu mengganti detail Wi-Fi, token, dan chat ID-mu sendiri.
Ke pin mana sensor PIR disambungkan di ESP32-CAM?
Kalau kamu tidak memakai kartu SD, GPIO 13 adalah pilihan paling aman: pin ini tidak memengaruhi boot dan, karena merupakan pin RTC, bisa membangunkan board dari deep sleep. Sambungkan VCC PIR ke pin 5V, GND ke GND, dan OUT ke GPIO 13. Hindari GPIO 12; kalau pin ini HIGH ketika boot, board tidak mau menyala.
Berapa lama ESP32-CAM bisa bertahan dengan baterai?
Dengan Wi-Fi menyala terus, sel 18650 2600 mAh bertahan sekitar 9 jam. Kalau kamu memakai deep sleep dan hanya bangun ketika ada gerakan, angka itu naik menjadi sekitar 10 hari; bahkan ketika tidur, board tetap menarik sekitar 6 mA karena regulator dan PSRAM-nya. Untuk berjalan berbulan-bulan, kamu butuh rangkaian power latch yang memutus catu daya board sepenuhnya.
Bisakah ESP32-CAM melihat di malam hari?
Filter IR-cut pada modul standar menghalangi cahaya inframerah, jadi dalam gelap kamu hanya mendapat gambar hingga 1–2 meter dengan flash putih. Dengan modul OV2640 tanpa filter (night vision) dan iluminator IR 850 nm, kamu bisa mengambil foto hitam-putih yang jelas dalam gelap. Menaikkan batas atas gain di pengaturan kamera juga membantu, tapi menambahkan bintik ke gambar.
Bisakah ESP32-CAM mengirim video ke Telegram?
Secara praktis, tidak. ESP32-CAM menghasilkan frame JPEG satu per satu dan tidak punya daya proses maupun memori untuk mengubahnya menjadi video MP4. Mengirim beberapa foto berturut-turut atau menonton live stream di jaringan rumahmu dengan CameraWebServer adalah pilihan yang lebih realistis.
Bisakah fotonya dikirim ke lebih dari satu orang?
Bisa. Kalau kamu menambahkan bot ke grup Telegram dan memakai ID grup, yang diawali tanda minus, sebagai CHAT_ID, foto akan sampai ke semua anggota grup sekaligus. Alternatifnya, masukkan beberapa chat ID ke sebuah array di kode dan kirim foto ke masing-masing secara bergantian; setiap pengiriman adalah upload terpisah, jadi waktunya lebih lama.
Proyek untuk dibuat setelah membaca ini
Lihat semua proyek
Sistem Alarm Gerakan ESP32 (PIR + Buzzer + LED + Telegram)
Sistem alarm keamanan pintar yang menyalakan buzzer dan LED ketika sensor PIR mendeteksi gerakan, sekaligus mengirim notifikasi instan ke pemilik lewat Telegram. Cooldown 30 detik membatasi seberapa sering alarm berulang; mekanisme ini tidak memvalidasi deteksi PIR atau menghilangkan pemicu palsu. Cocok untuk rumah, garasi, gudang, atau ruang kerja. Dengan ESP32-CAM bisa ditambahkan notifikasi berfoto; dengan ESP32 DevKit V1 klasik, notifikasinya berupa teks.

Lampu Otomatis dengan Sensor Gerak PIR ESP32
Di proyek ini, sensor gerak PIR HC-SR501 mendeteksi ketika seseorang masuk ke ruangan dan otomatis menyalakan LED. Lama LED menyala dilacak dengan millis(); setiap bacaan HIGH memperbarui waktu gerakan terakhir, dan LED mati minimal 10 detik setelah bacaan HIGH terakhir. Kode yang sekarang juga menunggu 50 ms di setiap loop dengan delay(50), jadi loopnya belum sepenuhnya non-blocking. Proyek ini bisa dipakai untuk lampu tangga.

Deep Sleep ESP32: Hemat Baterai (OLED + Penghitung RTC RAM)
Mode Deep Sleep ESP32 menurunkan konsumsi arus hingga ~10 µA dengan mematikan CPU dan sebagian besar periferal, sehingga sangat penting untuk perangkat IoT bertenaga baterai. Di proyek ini, kita memakai tiga sumber bangun yang berbeda: timer (15 detik), pin eksternal (EXT0, tombol GPIO 33), dan sentuhan (T0/GPIO 4). Variabel yang ditandai RTC_DATA_ATTR dipertahankan selama deep sleep; penghitung boot, penghitung bangun karena timer, dan penghitung bangun karena tombol terus dilacak.

Lampu Pintar ESP32 (PIR + LDR + Relay + OLED)
Ubah lorong, kamar mandi, atau gudangmu menjadi lampu pintar dengan ESP32! Dengan sensor gerak PIR, sensor cahaya LDR, dan modul relay, kita membuat skenario klasik "lampu menyala ketika ada gerakan dalam gelap"; jika sudah terang, gerakan TIDAK menyalakan lampu, jadi energi hemat di siang hari. Lampu menyala 30 detik; jika gerakan berlanjut, timer diulang. OLED SSD1306 menampilkan status dengan font besar (NYALA / SIANG / MENUNGGU) dan hitung mundur.

Koneksi Wi-Fi STA ESP32 (dengan Layar LCD)
Di proyek ini, kita menyambungkan ESP32 ke jaringan Wi-Fi rumah dalam mode Station (STA) dengan cara yang aman dan tahan gangguan. Kita menyimpan SSID dan kata sandi di file secrets.h terpisah agar tidak bocor ke Git, menyiapkan mekanisme sambung ulang otomatis, dan menampilkan tingkat sinyal RSSI dan alamat IP di layar LCD I2C. LED bawaan bekerja sebagai indikator status. Ini adalah pengelola koneksi kelas produksi.

mDNS ESP32: Mengakses Perangkat lewat bluegrays-esp.local (dengan Layar LCD)
Di proyek ini, kita menambahkan layanan mDNS (Multicast DNS) ke ESP32, sehingga perangkat bisa diakses dengan nama host seperti bluegrays-esp.local, bukan alamat IP. Meskipun IP dari DHCP berubah, perangkat selalu bisa dijangkau dengan nama yang sama. Web server kecil di perangkat menerima perintah kendali LED dari browser lewat alamat http://bluegrays-esp.local/. Layar LCD I2C menampilkan nama host, alamat IP, dan status LED.

Pendeteksi Gerakan dengan Sensor Gerak
Penjaga sederhana yang memberi tahu ketika ada orang masuk ke ruangan. Sensor gerak menangkap panas yang dipancarkan tubuh manusia, dan Arduino menjaga LED tetap menyala selama ada gerakan lalu memati
Artikel lainnya
Aplikasi BlueGrays
Rakit rangkaian ini langkah demi langkah di ponselmu
Skema pengkabelan, kode, dan daftar komponen dalam satu layar. 200+ proyek Arduino, ESP32, dan Pico siap dibuat.
Pindai dengan ponselmu
Arahkan kamera ke kode ini dan toko aplikasi di ponselmu akan terbuka.
atau buka toko aplikasi

