Panduan · 10 mnt baca

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.

Modul kamera ESP32-CAM untuk sistem foto Telegram
ESP32-CAM: kamera OV2640, LED flash, dan Wi-Fi dalam satu board. Dengan sensor PIR, board ini bisa mengirim foto ke Telegram ketika gerakan terdeteksi.

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.

KomponenSpesifikasiCatatan
ESP32-CAM (AI-Thinker)OV2640, PSRAM 4 MBVersi dengan PSRAM sanggup menangani foto XGA dengan mudah
ESP32-CAM-MB atau konverter USB-TTLCH340 / CP2102Untuk meng-upload kode; board-nya tidak punya USB
Sensor PIRHC-SR501 (5 V) atau AM312 (3,3 V)Sinyal OUT-nya 3,3 V dan langsung tersambung ke ESP32
Catu daya5 V, minimal 2 AWi-Fi dan flash menarik arus tinggi secara bersamaan
Kapasitor elektrolit470–1000 µFDi antara 5V dan GND, dekat dengan board
OpsionalSel 18650 + konverter boost 5 V, iluminator IR 850 nmUntuk 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.

PinTanpa kartu SDBisa membangunkan dari deep sleep?Catatan
GPIO 13BebasYa (ext0)Pilihan terbaik untuk PIR
GPIO 14BebasYaBisa memunculkan sinyal PWM singkat ketika boot; aman sebagai input
GPIO 15BebasYaPin strapping; kalau ditahan LOW, pesan boot jadi tidak muncul
GPIO 2BebasYaPin strapping; tidak boleh HIGH selama upload
GPIO 12BebasYaKalau HIGH ketika boot, board tidak mau menyala; jangan dipakai
GPIO 4LED flashYaKhusus untuk flash; jangan dipakai untuk hal lain
GPIO 3 (U0R)RX serialTidakHanya kalau kamu tidak butuh port serial
GPIO 16PSRAMTidakJangan pernah dipakai
Pin PIRPin ESP32-CAMKeterangan
VCC5VHC-SR501 butuh minimal 4,5 V; pakai 3V3 untuk AM312
OUTGPIO 133,3 V ketika ada gerakan, 0 V ketika tidak ada
GNDGNDGround bersama
Pin board yang berlabel VCC bukan input, melainkan output dari regulator 3,3 V; selalu berikan 5 V ke pin 5V untuk memberi daya ke PIR atau board. Jangan pernah menyambungkan PIR ke GPIO 12: kalau sensor mengeluarkan HIGH ketika boot, ESP32 mencoba menjalankan memori flash-nya dengan tegangan yang salah dan board sama sekali tidak mau menyala.

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

  1. Buka @BotFather di Telegram, ketik /newbot, lalu beri bot-mu nama dan username yang diakhiri dengan bot.
  2. Salin token dari BotFather, yang bentuknya seperti 1234567890:AAE...; token ini masuk ke baris BOT_TOKEN di kode.
  3. Untuk mengetahui nomor penggunamu sendiri, kirim /start ke @userinfobot; nilai Id yang dibalasnya adalah CHAT_ID-mu.
  4. Buka bot-mu sendiri dan kirim /start sekali; Telegram tidak mengizinkan bot mengirim pesan ke orang yang belum pernah mengobrol dengannya.
  5. Di Library Manager Arduino IDE, instal library UniversalTelegramBot (Brian Lough) dan ArduinoJson.
  6. Pilih AI Thinker ESP32-CAM di Tools > Board dan pastikan Partition Scheme-nya Huge APP (3MB No OTA/1MB SPIFFS).
Token adalah kata sandi bot-mu: selalu hapus baris itu sebelum membagikan kodemu atau meng-upload-nya ke mana pun. Kalau kamu curiga token-nya bocor, minta token baru dengan perintah /revoke di BotFather. Pembuatan bot, chat ID, dan sertifikat kami bahas lebih dalam di bot Telegram ESP32.

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

C++ (Arduino IDE)
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}
BagianFungsinya 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.

C++ (Arduino IDE)
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.

SkenarioArus 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.

C++ (Arduino IDE)
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

GejalaKemungkinan penyebabSolusi
Pesan masuk tapi fotonya tidakFrame terlalu besar, PSRAM mati, atau upload terputus di tengah jalanPilih board AI Thinker ESP32-CAM, turunkan resolusi ke SVGA, perkuat sinyal Wi-Fi
Bot tidak menjawab perintah apa punToken atau CHAT_ID salah, atau kamu belum pernah mengirim /start ke botSalin ulang token-nya, cek ID-mu dengan @userinfobot, kirim /start ke bot-mu
Foto gerakan datang tanpa alasanPIR terganggu sinyal dari antena Wi-Fi atau oleh sumber panasJauhkan 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 tidurOutput PIR masih HIGH ketika board tidurTunggu pin kembali ke LOW sebelum tidur, atur trimpot waktu HC-SR501 ke minimum
Flash menyala redup selama tidurGPIO 4 dibiarkan mengambangTulis LOW dan panggil rtc_gpio_hold_en(GPIO_NUM_4) sebelum tidur
Foto gelap atau kehijauanFrame-frame pertama tepat setelah kamera menyalaAmbil dan buang 2–3 frame sebelum foto yang sebenarnya
Board restart ketika mengirim fotoBrownout: catu daya tidak sanggup mengimbangi arus Wi-Fi dan flashPakai adaptor 5 V / 2 A, kabel pendek, dan kapasitor 470–1000 µF di antara 5V-GND
Kalau mengirim ke grup, ID grupnya diawali tanda minus (misalnya -1001234567890); tulis di baris CHAT_ID lengkap dengan tandanya. Agar bot bisa membaca pesan grup, kamu mungkin perlu mematikan /setprivacy di BotFather atau mengirim perintah dalam bentuk /foto@nama_bot; kode ini sudah membuang akhiran semacam itu.

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.

Lihat semua proyek
Sistem Alarm Gerakan ESP32 (PIR + Buzzer + LED + Telegram)
#145Mahir
Premium

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.

0.0(0)
9
Lampu Otomatis dengan Sensor Gerak PIR ESP32
#121Menengah
Premium

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.

4.4(24)
38
Deep Sleep ESP32: Hemat Baterai (OLED + Penghitung RTC RAM)
#135Mahir
Premium

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.

4.3(10)
16
Lampu Pintar ESP32 (PIR + LDR + Relay + OLED)
#141Mahir
Premium

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.

4.3(6)
11
Koneksi Wi-Fi STA ESP32 (dengan Layar LCD)
#127Mahir

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.

4.4(21)
34
mDNS ESP32: Mengakses Perangkat lewat bluegrays-esp.local (dengan Layar LCD)
#131Mahir
Premium

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.

4.8(14)
23
Pendeteksi Gerakan dengan Sensor Gerak
#24Pemula
Premium

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

4.6(47)
67