Panduan · 17 mnt baca

Sistem Alarm Arduino: Panduan PIR, Sensor Pintu, Keypad, dan Sirine

Buat sistem alarm Arduino dengan PIR, sensor pintu reed, zona getaran dan laser, kode keypad, jeda masuk dan keluar, relay sirine, serta notifikasi Telegram ESP32.

Sistem alarm Arduino adalah perangkat keamanan yang bisa kamu rakit sendiri: alat ini membunyikan sirine ketika ada orang masuk ke rumah atau kamarmu tanpa izin, memberimu beberapa detik untuk mengetik kode ketika pintu dibuka, dan kalau kamu mau, langsung mengirim notifikasi ke ponselmu. Di panduan ini kamu akan merakit alarm empat zona dengan sensor gerak PIR, kontak reed di pintu, sensor getaran yang menangkap ketukan di kaca jendela, dan tripwire laser yang melintang di lorong. Kamu akan mengaktifkan dan menonaktifkannya dengan kode di keypad 4x4, menyalakan sirine 12 V lewat relay, dan menjalankan semuanya dari satu Arduino Uno.

Langkah demi langkah kita akan membuat versi kecil dari apa yang dikerjakan panel alarm komersial: jeda masuk dan jeda keluar, mode di rumah (mode malam), penguncian setelah kode salah, batas waktu sirine, dan memori alarm. Alih-alih blok if yang bersarang, kodenya kita tulis sebagai state machine, sehingga apa yang sedang dikerjakan alarm selalu jelas di setiap momen. Setelah itu kamu akan memindahkan logika yang sama ke ESP32 untuk mengaktifkan, menonaktifkan, dan menerima notifikasi alarm lewat Telegram, menulis versi MicroPython untuk Raspberry Pi Pico, dan belajar cara mengurangi masalah yang paling menyebalkan: alarm palsu.

Sensor gerak PIR HC-SR501
Sensor gerak PIR HC-SR501 adalah zona interior alarm: lensa Fresnel putihnya membagi bidang pandang menjadi puluhan zona kecil, dan dua trimpot di sebelahnya mengatur jarak deteksi dan lama output.

Bagaimana cara kerja sistem alarm Arduino?

Setiap sistem alarm punya tiga bagian: input (sensor), otak yang mengambil keputusan (Arduino), dan output (sirine, buzzer, notifikasi). Setiap sensor menjaga satu zona. Panel profesional mengelompokkan zona berdasarkan perilakunya ketika terpicu, dan perbedaan inilah yang memisahkan alarm yang bagus dari gadget menyebalkan yang berbunyi setiap kali pintu dibuka. Sistem kita akan punya empat zona:

ZonaSensorJenis zonaJika terpicu ketika aktif
Pintu depanKontak reed (MC-38)TertundaMemulai jeda masuk 20 dtk; alarm jika kode tidak dimasukkan
Ruang tamu, interiorPIR (HC-SR501)Interior (follower)Alarm langsung; menunggu jika jeda masuk sedang berjalan; mati di mode di rumah
JendelaGetaran (SW-420)LangsungAlarm langsung setelah 3 ketukan dalam 3 dtk
Lorong, tanggaLaser + LDR (KY-008)Interior (follower)Alarm langsung; menunggu jika jeda masuk sedang berjalan
Gas, api, tamperMQ-2, sensor api, sakelar kotak24 jamAlarm bahkan ketika nonaktif (ide pengembangan)

Berkat zona tertunda, sirine tidak langsung berbunyi begitu kamu pulang: pintu terbuka, buzzer berbunyi bip untuk mengingatkanmu, dan kamu punya 20 detik untuk mengetik kode. Zona interior follower (pengikut) bersabar selama jeda masuk berjalan, jadi melewati PIR dalam perjalanan dari pintu ke keypad tidak memicu alarm. Tapi kalau seseorang masuk tanpa membuka pintu, misalnya lewat jendela, lalu melangkah di depan PIR, jeda masuk tidak pernah dimulai dan alarm langsung berbunyi. Zona langsung selalu terpicu seketika; tidak ada gunanya memberi 20 detik kepada orang yang sedang membobol jendela. Ketika keluar, cara kerjanya mirip: begitu kamu mengaktifkan sistem, jeda keluar 30 detik dimulai, dan keluar lewat pintu selama waktu itu tidak memicu apa pun.

Daftar komponen sistem alarm Arduino

KomponenJmlFungsi
Arduino Uno (atau Nano)1Otak sistem; pin di panduan ini sama di Uno dan Nano
Sensor gerak PIR HC-SR5011Deteksi gerakan di dalam ruangan, 3–7 m, sekitar 110°
Kontak pintu reed MC-38 (atau reed switch + magnet)1Memutus rangkaian ketika pintu atau jendela terbuka
Modul sensor getaran SW-4201Mendeteksi ketukan di kaca atau kusen
Modul laser KY-008 + LDR + resistor 10 kΩ1 setPenghalang cahaya yang melintang di lorong
Keypad membran 4x41Mengaktifkan dan menonaktifkan dengan kode
Buzzer aktif (5 V)1Bunyi klik tombol, peringatan masuk dan keluar, sirine dalam ruangan
Modul relay 5 V 1 kanal1Menghidupmatikan sirine 12 V, terisolasi dari Arduino
Sirine 12 V (idealnya dengan lampu strobo)1Suara alarm utama, biasanya 100–120 dB
LED merah dan hijau + 2 × resistor 220 Ω1 setIndikator aktif dan siap
Catu daya 12 V, modul LM2596, aki 12 V (opsional)1 setDaya dan cadangan ketika listrik padam
Breadboard, kabel jumper, kabel alarm 4 inti-Sambungan dan sensor jarak jauh
Arduino Uno
Arduino Uno
Reed
Reed
SW-420
SW-420
KY-008
KY-008
Keypad 4x4
Keypad 4x4
Buzzer
Buzzer
Relay 5 V
Relay 5 V

Sensor: apa yang ditangkap setiap zona?

  • PIR (HC-SR501): Mendeteksi perubahan radiasi inframerah yang dipancarkan tubuh manusia dan melihat gerakan pada jarak 3–7 meter dengan sudut sekitar 110 derajat. Output-nya 3,3 V, tapi butuh catu daya 5 V. Selama 30–60 detik setelah dinyalakan, sensor ini mempelajari ruangan dan bisa terpicu sendiri, karena itulah kode mengabaikan 60 detik pertama. Detailnya ada di artikel sensor gerak PIR.
  • Kontak pintu reed (MC-38): Dua rumah plastik, satu disekrup ke kusen dan satu ke daun pintu. Ketika pintu tertutup, magnetnya dekat dan kontaknya tertutup; begitu pintu terbuka lebih dari 15–25 mm, kontaknya terbuka. Dibaca dengan satu pin dan pull-up internal; isinya sudah kita bahas di artikel reed switch.
  • Sensor getaran (SW-420): Sakelar normally closed dengan batang logam yang dikelilingi pegas. Ketika kaca atau kusen terkena ketukan, kontaknya terbuka sekejap dan komparator LM393 di board mengubahnya menjadi pulsa digital. Pada kebanyakan modul, output-nya LOW ketika diam dan sebentar menjadi HIGH ketika ada ketukan; cek modulmu sendiri dengan tes sensor. Sensitivitasnya diatur dengan trimpot di board; detailnya di artikel sensor getaran SW-420.
  • Tripwire laser (KY-008 + LDR): Laser 650 nm sekitar 5 mW menyinari terus-menerus sebuah LDR di sisi lain lorong. Ketika sinarnya terputus, hambatan LDR melonjak dari beberapa ratus ohm menjadi puluhan kiloohm dan pembacaan analognya turun tajam. Tips pemasangan dan penyelarasan ada di artikel modul laser KY-008.
  • Keypad 4x4: Matriks 16 tombol yang terdiri dari 4 jalur baris dan 4 jalur kolom; memakai 8 pin dan dibaca dalam satu baris kode dengan library Keypad. Cara kerja pemindaian baris-kolom bisa kamu temukan di panduan keypad.
  • Sirine dan buzzer: Buzzer aktif punya osilator sendiri, jadi langsung berbunyi bip begitu pin-nya kamu jadikan HIGH; cocok untuk bunyi klik tombol serta peringatan masuk dan keluar. Suara yang benar-benar menakutkan pencuri datang dari sirine 12 V, yang kamu hidupmatikan lewat modul relay. Untuk perbedaan buzzer aktif dan pasif, lihat apa itu buzzer.
KY-008 adalah laser kelas 3R; menatap sinarnya langsung atau lewat cermin bisa merusak matamu. Selalu pasang sinarnya di bawah tinggi mata, misalnya 20–40 cm di atas lantai. Ketinggian itu aman dan juga memastikan siapa pun yang masuk pasti memutus sinarnya dengan langkahnya.

Diagram pengkabelan dan tabel pin

Tabel di bawah memberikan pengkabelan untuk ketiga board. Versi Arduino Uno memakai keempat zona dan keypad. Karena versi ESP32 diaktifkan dan dinonaktifkan dengan perintah Telegram, bukan keypad, dan versi Pico dibuat ringkas, tripwire laser hanya ada di Uno. Untuk melihat nomor pin board-mu sendiri, pakai alat Pinout, dan untuk menghitung resistor LED, pakai kalkulator resistor LED.

SambunganArduino UnoESP32Raspberry Pi Pico
Keypad R1–R4 (baris)D9, D8, D7, D6– (Telegram)GP2, GP3, GP4, GP5
Keypad C1–C4 (kolom)D5, D4, D3, D2– (Telegram)GP6, GP7, GP8, GP9
PIR VCC / OUT / GND5V / D10 / GNDVIN (5V) / GPIO 27 / GNDVBUS / GP16 / GND
Kontak reed (2 kabel)D11 dan GNDGPIO 33 dan GNDGP15 dan GND
SW-420 VCC / DO / GND5V / D12 / GND3V3 / GPIO 32 / GND3V3(OUT) / GP17 / GND
KY-008 S / − (laser selalu menyala)5V / GND––
LDR antara 5V dan A0, 10 kΩ antara A0 dan GNDA0––
Buzzer aktif + (− ke GND)A1GPIO 25GP14
IN modul relayA2GPIO 26GP13
LED hijau (dengan 220 Ω): siapA3––
LED merah (dengan 220 Ω): aktifD13GPIO 2 (LED bawaan)LED bawaan
Jangan pernah menyambungkan sirine 12 V langsung ke pin Arduino. Kutub positif catu daya masuk ke terminal COM relay, terminal NO relay ke kutub positif sirine, dan kutub negatif sirine kembali ke kutub negatif catu daya, sehingga arus sirine tidak pernah mengalir lewat Arduino. Pin 3,3 V pada ESP32 dan Pico tidak bisa melepas sepenuhnya beberapa modul relay 5 V, dan relay bisa tetap dialiri arus; di board ini pakai modul yang kompatibel 3,3 V atau bertrigger high-level, atau driver transistor NPN.

Alarm paling dibutuhkan justru ketika listrik padam, jadi pikirkan catu dayanya sejak awal. Susunan yang praktis: persis seperti panel profesional, catu daya alarm 13,8 V dengan sambungan aki memberi daya ke sirine dan, setelah diturunkan ke 5 V dengan buck converter LM2596, ke pin 5V Arduino. Ketika listrik PLN padam, aki kering (sealed lead-acid) 12 V 7 Ah mengambil alih. Hitungannya sederhana: Uno, PIR, laser, LED, dan SW-420 menarik sekitar 100 mA pada 5 V dalam keadaan standby; itu 0,5 W, yang dengan konverter berefisiensi 85% kira-kira 50 mA di sisi aki. Meskipun kamu hanya memakai 80% kapasitasnya, aki 7 Ah membuat sistem tetap jalan selama 4–5 hari; arus 0,2–0,5 A yang ditarik sirine ketika berbunyi sedikit memperpendeknya. Coba angkamu sendiri dengan kalkulator umur baterai, dan lihat Cara Memberi Daya ke Arduino untuk pilihan catu daya.

Merakit langkah demi langkah

  1. Di Arduino IDE, buka Library Manager, ketik Keypad, lalu instal library buatan Mark Stanley dan Alexander Brevig. Tanpa library ini kamu akan mendapat error Keypad.h: No such file or directory ketika mengompilasi.
  2. Pegang keypad dengan tombol menghadap ke arahmu; sambungkan empat kabel pertama dari kiri (R1–R4) ke D9, D8, D7, D6 dan empat berikutnya (C1–C4) ke D5, D4, D3, D2.
  3. Sambungkan PIR ke 5V, D10, dan GND; putar kedua trimpot penuh berlawanan arah jarum jam dan pasang jumper trigger di posisi H.
  4. Sambungkan dua kabel kontak reed ke D11 dan GND, dan SW-420 ke 5V, D12, dan GND.
  5. Pasang LDR antara 5V dan A0 dan resistor 10 kΩ antara A0 dan GND; letakkan LDR di dasar tabung hitam dan arahkan laser ke tengah tabung.
  6. Sambungkan buzzer ke A1, IN modul relay ke A2, serta VCC dan GND-nya ke 5V dan GND Arduino; sambungkan LED ke A3 dan D13 lewat resistor 220 Ω.
  7. Upload dulu tes sensor di bawah, buka Serial Monitor di 9600 baud, lalu picu setiap sensor satu per satu.
  8. Setelah itu upload sketch alarm lengkap, ganti kodenya, dan uji sistem hanya dengan buzzer selama beberapa hari sebelum menyambungkan sirine.

Tes pertama: melihat setiap sensor di Serial Monitor

Sebelum beralih ke kode alarm, pastikan setiap sensor berperilaku sesuai harapanmu. Sketch kecil ini mencetak status PIR, apakah pintu terbuka atau tertutup, jumlah pulsa getaran dalam setengah detik terakhir, dan nilai analog LDR dua kali per detik. Pin getaran dibaca terus-menerus karena pulsa SW-420 bisa hanya berlangsung beberapa milidetik, dan kode yang memakai delay() di sela-selanya akan melewatkannya.

C++ (Arduino IDE)
1// Alarm sensör testi: tüm girişleri saniyede iki kez yazdırır / Alarm sensor test: prints every input twice a second
2const int PIR_PIN = 10;        // HC-SR501 OUT
3const int KAPI_PIN = 11;       // reed kapı kontağı, diğer ucu GND / reed door contact, other leg to GND
4const int TITRESIM_PIN = 12;   // SW-420 DO
5const int LAZER_PIN = A0;      // LDR bölücünün orta noktası / LDR divider midpoint
6
7int titresimSayisi = 0;        // yarım saniyedeki darbe sayısı / pulses in the last half second
8int oncekiTitresim = LOW;
9unsigned long sonYazma = 0;
10
11void setup() {
12  Serial.begin(9600);
13  pinMode(PIR_PIN, INPUT);
14  pinMode(KAPI_PIN, INPUT_PULLUP);   // kapı kapalı = LOW, açık = HIGH / door closed = LOW, open = HIGH
15  pinMode(TITRESIM_PIN, INPUT);
16}
17
18void loop() {
19  int t = digitalRead(TITRESIM_PIN);  // kısa darbeleri kaçırmamak için sürekli oku / read constantly so short pulses are not missed
20  if (t == HIGH && oncekiTitresim == LOW) titresimSayisi++;
21  oncekiTitresim = t;
22
23  if (millis() - sonYazma >= 500) {
24    sonYazma = millis();
25    Serial.print(F("PIR: "));
26    Serial.print(digitalRead(PIR_PIN));
27    Serial.print(F("  Kapi / door: "));
28    Serial.print(digitalRead(KAPI_PIN) == HIGH ? F("ACIK / open") : F("kapali / closed"));
29    Serial.print(F("  Titresim / vibration: "));
30    Serial.print(titresimSayisi);
31    Serial.print(F("  Lazer / laser: "));
32    Serial.println(analogRead(LAZER_PIN));
33    titresimSayisi = 0;
34  }
35}
Tes ini juga memberimu ambang laser: catat pembacaan ketika sinar mengenai LDR (mis. 950) dan pembacaan ketika tanganmu menghalangi sinar (mis. 250), lalu atur LAZER_ESIK (ambang laser) di kode alarm ke nilai tengahnya (600). Kalau selisih keduanya kurang dari 300, perpanjang tabungnya atau selaraskan ulang laser.

State machine: otak alarm

Kalau kamu mencoba menulis alarm dengan blok if dan delay(), kodenya cepat berubah jadi kusut: keypad tidak dibaca selama jeda masuk menunggu, dan kamu tidak bisa mengetik kode ketika sirine berbunyi. Solusinya adalah state machine, di mana sistem berada tepat di satu state pada setiap momen dan berpindah antar-state karena kejadian tertentu. Di kode, state didefinisikan dengan enum, loop() hanya menerapkan aturan state yang sedang berlaku di setiap putaran, dan waktu tunggu diukur dengan millis(), bukan delay(). Dengan begitu loop berjalan ribuan kali per detik dan tidak ada tekanan tombol atau kejadian sensor yang terlewat.

StateDimasuki ketikaApa yang dilakukanDitinggalkan ketika
DISARMED (nonaktif)Ketika dinyalakan dan setelah kode + #Memantau sensor tapi tidak pernah membunyikan alarm; LED hijau menyala ketika semuanya siapKode + A atau kode + B
EXIT DELAY (jeda keluar)Kode + A (pergi) atau B (di rumah)Menghitung mundur 30 dtk; buzzer berbunyi bip sekali per detik dan makin cepat di 10 dtk terakhirARMED ketika waktunya habis; DISARMED dengan kode + #
ARMED (aktif)Ketika jeda keluar berakhirMemantau semua zona; LED merah tetap menyalaENTRY jika pintu terbuka; ALARM pada zona langsung atau interior
ENTRY DELAY (jeda masuk)Pintu terbuka ketika aktifBuzzer berbunyi bip selama 20 dtk; PIR dan laser ditahanDISARMED dengan kode + #; ALARM ketika waktunya habis
ALARMSebuah zona terpicuSirine dan buzzer berbunyi; sirine berhenti setelah 3 mnt, LED merah berkedip cepatDISARMED hanya dengan kode + #

Mengaktifkan, menonaktifkan, dan kode di keypad

TombolFungsi
2580 + AMode pergi: keempat zona aktif, jeda keluar 30 dtk
2580 + BMode di rumah (malam): PIR mati; pintu, jendela, dan laser aktif
2580 + #Menonaktifkan; jika tadi ada alarm, mencetak zona pemicunya ke Serial Monitor
* atau CMenghapus kode yang salah ketik
DTes zona: mencetak nilai pintu, PIR, dan laser ke Serial Monitor
3 kode salahKeypad terkunci selama 60 dtk; selama itu kode yang benar pun ditolak

Meminta kode ketika mengaktifkan juga mencegah anak-anak atau tamu mengaktifkan sistem secara tidak sengaja. Kodenya juga melakukan pengecekan belum siap (not ready): kalau kamu mencoba mengaktifkan ketika pintu terbuka atau sinar laser terputus, kamu mendengar bunyi bip panjang dan sistem tidak aktif, persis seperti di panel komersial. Jangan biarkan kode tetap di bawaan 2580, dan hindari kode yang mudah ditebak seperti 1234, 0000, atau tahun lahirmu. Kalau kamu ingin mengganti kode tanpa meng-upload ulang sketch, simpan kodenya di EEPROM dan tambahkan menu untuk memasukkan kode baru dari keypad. Tambahkan layar LCD I2C di sebelah keypad, dan kamu juga bisa melihat hitung mundur dan pesan status di layar; karena itulah pin A4 dan A5 dibiarkan kosong.

Kode sistem alarm Arduino

Kode di bawah adalah inti panduan ini. Di bagian pengaturan paling atas kamu bisa mengganti kode, jeda, lama sirine, ambang laser, dan jenis relay; sisanya tidak perlu disentuh. Di Arduino Uno, kode ini memakai sekitar 6 KB memori program dan 620 byte RAM, jadi masih banyak ruang untuk LCD atau pembaca RFID.

C++ (Arduino IDE)
1// Arduino ev alarm sistemi: 4 bölge, keypad, giriş/çıkış gecikmesi, siren / Arduino home alarm: 4 zones, keypad, entry/exit delay, siren
2#include <Keypad.h>
3
4// ---- Ayarlar / Settings ----
5const char SIFRE[] = "2580";                 // kendi şifreni yaz / set your own code
6const unsigned long CIKIS_SURE = 30000;      // çıkış gecikmesi 30 sn / exit delay 30 s
7const unsigned long GIRIS_SURE = 20000;      // giriş gecikmesi 20 sn / entry delay 20 s
8const unsigned long SIREN_SURE = 180000;     // siren en fazla 3 dk çalar / siren sounds for at most 3 min
9const unsigned long PIR_ISINMA = 60000;      // PIR ilk 60 sn yok sayılır / PIR is ignored for the first 60 s
10const unsigned long KILIT_SURE = 60000;      // 3 yanlış şifrede 60 sn kilit / 60 s lockout after 3 wrong codes
11const int LAZER_ESIK = 600;                  // ışınlı ve ışınsız değerin ortası / midpoint of beam and no-beam readings
12const bool LAZER_VAR = true;                 // lazer bariyerin yoksa false yap / set to false without a laser barrier
13const bool ROLE_AKTIF_LOW = true;            // çoğu röle modülü LOW'da çeker / most relay modules switch on with LOW
14
15// ---- Pinler / Pins ----
16const int PIR_PIN = 10;        // HC-SR501 OUT
17const int KAPI_PIN = 11;       // reed, diğer ucu GND / reed, other leg to GND
18const int TITRESIM_PIN = 12;   // SW-420 DO
19const int LAZER_PIN = A0;      // LDR bölücü / LDR divider
20const int BUZZER_PIN = A1;     // aktif buzzer / active buzzer
21const int ROLE_PIN = A2;       // siren rölesi / siren relay
22const int YESIL_LED = A3;      // hazır / ready
23const int KIRMIZI_LED = 13;    // kurulu / armed
24
25const byte SATIR = 4, SUTUN = 4;
26char tuslar[SATIR][SUTUN] = {
27  {'1', '2', '3', 'A'},
28  {'4', '5', '6', 'B'},
29  {'7', '8', '9', 'C'},
30  {'*', '0', '#', 'D'}
31};
32byte satirPinleri[SATIR] = {9, 8, 7, 6};     // R1–R4
33byte sutunPinleri[SUTUN] = {5, 4, 3, 2};     // C1–C4
34Keypad keypad = Keypad(makeKeymap(tuslar), satirPinleri, sutunPinleri, SATIR, SUTUN);
35
36enum Durum { COZULU, CIKIS_GECIKMESI, KURULU, GIRIS_GECIKMESI, ALARM };
37const char* const DURUM_ADI[] = { "COZULU / DISARMED", "CIKIS / EXIT DELAY", "KURULU / ARMED", "GIRIS / ENTRY DELAY", "ALARM" };
38
39struct Giris {                 // gürültü süzgeçli giriş / debounced input
40  bool ham;
41  bool kararli;
42  unsigned long degisim;
43};
44Giris gKapi = {false, false, 0};
45Giris gPir = {false, false, 0};
46Giris gLazer = {false, false, 0};
47
48Durum durum = COZULU;
49unsigned long durumZamani = 0;     // duruma giriş anı / when the state began
50bool evdeModu = false;             // true: PIR devre dışı / true: PIR bypassed
51bool sirenAcik = false;
52bool pirHazir = false;
53const char* tetikleyen = "";       // alarmı başlatan bölge / zone that caused the alarm
54char girilen[9] = "";              // girilen şifre, en fazla 8 hane / typed code, max 8 digits
55byte haneSayisi = 0;
56byte yanlisSayisi = 0;
57bool kilitli = false;
58unsigned long kilitZamani = 0;
59unsigned long bipZamani = 0, bipSuresi = 0;
60unsigned long darbe[3] = {0, 0, 0};          // son 3 titreşim olayı / last 3 vibration events
61int oncekiTitresim = LOW;
62
63// Okuma "sure" ms boyunca aynı kalırsa kabul edilir / a reading is accepted once it stays the same for "sure" ms
64bool suz(Giris &g, bool okuma, unsigned long sure) {
65  unsigned long simdi = millis();
66  if (okuma != g.ham) {
67    g.ham = okuma;
68    g.degisim = simdi;
69  }
70  if (simdi - g.degisim >= sure) g.kararli = g.ham;
71  return g.kararli;
72}
73
74// 3 sn içinde 3 ayrı darbe = zorlama; tek vuruş alarm vermez / 3 separate hits within 3 s = forcing; a single knock is ignored
75bool titresimVar(unsigned long simdi) {
76  int okuma = digitalRead(TITRESIM_PIN);
77  bool sonuc = false;
78  if (okuma == HIGH && oncekiTitresim == LOW && simdi - darbe[2] > 150) {   // 150 ms içindeki sekmeler tek olay / bounces within 150 ms count once
79    darbe[0] = darbe[1];
80    darbe[1] = darbe[2];
81    darbe[2] = simdi;
82    if (darbe[0] != 0 && simdi - darbe[0] <= 3000) sonuc = true;
83  }
84  oncekiTitresim = okuma;
85  return sonuc;
86}
87
88void bip(unsigned long ms) {       // kısa = tuş sesi, uzun = hata / short = key click, long = error
89  bipZamani = millis();
90  bipSuresi = ms;
91}
92
93void sirenYaz(bool acik) {
94  sirenAcik = acik;
95  if (ROLE_AKTIF_LOW) digitalWrite(ROLE_PIN, acik ? LOW : HIGH);
96  else                digitalWrite(ROLE_PIN, acik ? HIGH : LOW);
97}
98
99void durumDegis(Durum yeni) {
100  durum = yeni;
101  durumZamani = millis();
102  Serial.print(F("Durum / state: "));
103  Serial.println(DURUM_ADI[yeni]);
104}
105
106void alarmVer(const char* bolge) {
107  tetikleyen = bolge;
108  sirenYaz(true);
109  durumDegis(ALARM);
110  Serial.print(F("ALARM! Bolge / zone: "));
111  Serial.println(bolge);
112}
113
114bool kurulabilirMi() {             // açık kapı ya da kesik ışınla kurulmaz / never arm with an open door or a broken beam
115  if (gKapi.kararli) {
116    Serial.println(F("Kapi acik, kurulamaz / door open, cannot arm"));
117    return false;
118  }
119  if (LAZER_VAR && gLazer.kararli) {
120    Serial.println(F("Lazer kesik, kurulamaz / beam broken, cannot arm"));
121    return false;
122  }
123  return true;
124}
125
126void kur(bool evde) {
127  if (!kurulabilirMi()) {
128    bip(800);
129    return;
130  }
131  evdeModu = evde;
132  for (int i = 0; i < 3; i++) darbe[i] = 0;
133  tetikleyen = "";
134  durumDegis(CIKIS_GECIKMESI);
135  Serial.println(evde ? F("Evde modu: PIR devre disi / stay mode: PIR bypassed") : F("Tam kurulum / away mode"));
136}
137
138void coz() {
139  sirenYaz(false);
140  if (tetikleyen[0] != '\0') {     // alarm hafızası / alarm memory
141    Serial.print(F("Alarm hafizasi / alarm memory: "));
142    Serial.println(tetikleyen);
143  }
144  durumDegis(COZULU);
145  bip(150);
146}
147
148void sensorRaporu() {
149  Serial.print(F("Kapi / door: "));
150  Serial.print(gKapi.kararli ? F("ACIK / open") : F("kapali / closed"));
151  Serial.print(F("  PIR: "));
152  Serial.print(digitalRead(PIR_PIN));
153  Serial.print(F("  Lazer / laser: "));
154  Serial.println(analogRead(LAZER_PIN));
155}
156
157void tusIsle(char t) {
158  if (t >= '0' && t <= '9') {
159    if (haneSayisi < 8) {
160      girilen[haneSayisi++] = t;
161      girilen[haneSayisi] = '\0';
162    }
163    bip(30);
164    return;
165  }
166  if (t == '*' || t == 'C') {      // girilen kodu sil / clear the typed code
167    haneSayisi = 0;
168    girilen[0] = '\0';
169    bip(30);
170    return;
171  }
172  if (t == 'D') {                  // bölge testi / zone test
173    sensorRaporu();
174    return;
175  }
176
177  // A, B ve # önce şifre ister / A, B and # need the code first
178  bool dogru = strcmp(girilen, SIFRE) == 0;
179  haneSayisi = 0;
180  girilen[0] = '\0';
181  if (kilitli) {
182    Serial.println(F("Tus takimi kilitli / keypad locked"));
183    bip(800);
184    return;
185  }
186  if (!dogru) {
187    Serial.println(F("Yanlis sifre / wrong code"));
188    if (++yanlisSayisi >= 3) {
189      kilitli = true;
190      kilitZamani = millis();
191      yanlisSayisi = 0;
192    }
193    bip(800);
194    return;
195  }
196  yanlisSayisi = 0;
197  if (durum == COZULU && t == 'A')      kur(false);   // tam kurulum / away
198  else if (durum == COZULU && t == 'B') kur(true);    // evde modu / stay
199  else if (durum != COZULU && t == '#') coz();        // çöz / disarm
200  else bip(800);
201}
202
203void cikislariGuncelle() {
204  unsigned long simdi = millis();
205  unsigned long gecen = simdi - durumZamani;
206  bool buzzer = simdi - bipZamani < bipSuresi;   // tuş sesi / key click
207  bool kirmizi = false;
208
209  switch (durum) {
210    case CIKIS_GECIKMESI: {
211      unsigned long periyot = (gecen > CIKIS_SURE - 10000) ? 250 : 1000;   // son 10 sn hızlanır / faster in the last 10 s
212      buzzer = buzzer || (gecen % periyot) < 60;
213      kirmizi = (gecen % 1000) < 500;
214      break;
215    }
216    case KURULU:
217      kirmizi = true;
218      break;
219    case GIRIS_GECIKMESI:
220      buzzer = buzzer || (gecen % 400) < 200;    // "şifreni gir" uyarısı / "enter your code" warning
221      kirmizi = (gecen % 400) < 200;
222      break;
223    case ALARM:
224      buzzer = buzzer || sirenAcik;
225      kirmizi = (gecen % 200) < 100;             // hızlı yanıp sönme = alarm hafızası / fast blink = alarm memory
226      break;
227    default:
228      break;
229  }
230  bool hazir = !gKapi.kararli && !(LAZER_VAR && gLazer.kararli);
231  digitalWrite(BUZZER_PIN, buzzer ? HIGH : LOW);
232  digitalWrite(KIRMIZI_LED, kirmizi ? HIGH : LOW);
233  digitalWrite(YESIL_LED, (durum == COZULU && hazir) ? HIGH : LOW);
234}
235
236void setup() {
237  Serial.begin(9600);
238  sirenYaz(false);                   // pin çıkış olmadan önce "kapalı" yaz, açılışta röle çekmez / write "off" first so the relay doesn't click at boot
239  pinMode(ROLE_PIN, OUTPUT);
240  pinMode(BUZZER_PIN, OUTPUT);
241  pinMode(KIRMIZI_LED, OUTPUT);
242  pinMode(YESIL_LED, OUTPUT);
243  pinMode(PIR_PIN, INPUT);
244  pinMode(KAPI_PIN, INPUT_PULLUP);   // kapı kapalı = LOW; kablo kesilirse HIGH / door closed = LOW; a cut wire reads HIGH
245  pinMode(TITRESIM_PIN, INPUT);
246  keypad.setDebounceTime(20);
247  durumDegis(COZULU);
248  Serial.println(F("PIR 60 sn isiniyor / PIR warming up for 60 s"));
249}
250
251void loop() {
252  char t = keypad.getKey();
253  if (t) tusIsle(t);
254
255  unsigned long simdi = millis();
256  if (kilitli && simdi - kilitZamani >= KILIT_SURE) kilitli = false;
257  if (!pirHazir && simdi >= PIR_ISINMA) pirHazir = true;
258
259  bool kapiAcik = suz(gKapi, digitalRead(KAPI_PIN) == HIGH, 50);
260  bool hareket = suz(gPir, digitalRead(PIR_PIN) == HIGH, 200) && pirHazir;
261  bool isinKesik = LAZER_VAR && suz(gLazer, analogRead(LAZER_PIN) < LAZER_ESIK, 30);
262  bool titresim = titresimVar(simdi);
263
264  switch (durum) {
265    case CIKIS_GECIKMESI:
266      if (simdi - durumZamani >= CIKIS_SURE) durumDegis(KURULU);
267      break;
268    case KURULU:                                   // anlık bölgeler önce / instant zones first
269      if (titresim)                  alarmVer("Pencere (titresim) / window (vibration)");
270      else if (isinKesik)            alarmVer("Lazer bariyeri / laser beam");
271      else if (hareket && !evdeModu) alarmVer("Ic mekan (PIR) / interior (PIR)");
272      else if (kapiAcik)             durumDegis(GIRIS_GECIKMESI);
273      break;
274    case GIRIS_GECIKMESI:                          // PIR ve lazer giriş süresince bekler / PIR and laser wait during the entry delay
275      if (titresim)                                 alarmVer("Pencere (titresim) / window (vibration)");
276      else if (simdi - durumZamani >= GIRIS_SURE)   alarmVer("Giris kapisi / entry door");
277      break;
278    case ALARM:
279      if (sirenAcik && simdi - durumZamani >= SIREN_SURE) {
280        sirenYaz(false);                           // siren susar, hafıza kalır / siren stops, memory stays
281        Serial.println(F("Siren sustu, sifre bekleniyor / siren off, waiting for the code"));
282      }
283      break;
284    default:
285      break;
286  }
287  cikislariGuncelle();
288}

Tulang punggung kode ini adalah fungsi suz() (dari bahasa Turki “saring”). Setiap kali nilai mentah sebuah input berubah, cap waktu dicatat, dan status stabilnya tidak berubah sampai nilainya tetap sama selama waktu tertentu (50 ms untuk pintu, 200 ms untuk PIR, 30 ms untuk laser). Filter sederhana ini mencegah gangguan singkat di kabel panjang dan pantulan kontak memicu alarm. Membaca pin pintu dengan INPUT_PULLUP juga pilihan yang disengaja: ketika pintu tertutup, reed menghubungkan pin ke GND dan terbaca LOW; ketika pintu terbuka atau seseorang memotong kabelnya, pin menjadi HIGH. Logika loop tertutup (closed-loop) yang dipakai di sistem alarm sungguhan ini memperlakukan kabel yang terpotong persis seperti pintu yang terbuka.

Getaran ditangani dengan cara berbeda. Satu ketukan saja membuat SW-420 memantul beberapa kali dalam beberapa milidetik; titresimVar() (“ada getaran”) pertama-tama menghitung pantulan dalam 150 ms sebagai satu kejadian, lalu menyimpan waktu tiga kejadian terakhir. Kalau tiga kejadian muat dalam 3 detik, itu dihitung sebagai upaya nyata membobol jendela; angin yang menggetarkan pintu atau satu bola yang mengenai kaca tidak akan memicunya. Sirine berhenti sendiri setelah 3 menit, tapi sistem tetap di state ALARM dan LED merah berkedip cepat untuk memberi tahu siapa pun yang pulang bahwa tadi ada alarm. Ketika kamu memasukkan kode yang benar, nama zona yang disimpan di variabel tetikleyen (“pemicu”) dicetak ke Serial Monitor; ini versi kecil dari memori alarm yang ada di panel komersial.

Cara mengatur jeda masuk dan jeda keluar

Kalau jedanya terlalu pendek, kamu akan memicu alarmmu sendiri; kalau terlalu panjang, kamu memberi waktu kepada pencuri. Buat jeda keluar dua kali waktu yang kamu butuhkan untuk berjalan dari keypad ke pintu lalu menguncinya, dan jeda masuk kira-kira dua kali waktu untuk membuka pintu, berjalan ke keypad, dan mengetik kode. Panel komersial biasanya memakai 30–60 detik untuk keluar dan 15–30 detik untuk masuk. Memasang keypad tepat di sebelah pintu, tapi tidak terlihat dari luar, membuat jeda masuk bisa tetap pendek. Perilaku buzzer juga penting: ketika keluar, bunyi bip pendek setiap detik makin cepat di 10 detik terakhir supaya kamu merasa waktunya hampir habis; ketika masuk, bunyi bip nyala-mati 200 ms mengingatkanmu untuk mengetik kode.

Pengkabelan sirine dan relay

Di rangkaian sirine, Arduino hanya menggerakkan kumparan relay; arus sirine 12 V mengalir lewat kontak relay. Kebanyakan modul relay bersifat aktif LOW, artinya menyala ketika pin IN bernilai LOW; karena itulah kode punya konstanta ROLE_AKTIF_LOW. Kode menulis level mati sebelum menjadikan pin sebagai output, sehingga relay tidak sempat klik sekejap dan membuat sirine berbunyi singkat ketika Arduino booting. Ketika memilih sirine, cek arusnya: sirine kecil 12 V 110 dB biasanya menarik 0,1–0,3 A, beban yang sangat kecil bagi kontak relay 10 A. Sirine luar ruangan dengan lampu strobo menakut-nakuti dengan suara sekaligus cahaya dan menunjukkan kepada tetangga dari mana alarm berbunyi. Panel profesional biasanya mematikan sirine setelah 3 menit, dan beberapa negara bahkan menetapkan batas hukum untuk ini, jadi atur SIREN_SURE (lama sirine) dengan wajar. Kalau kamu lebih suka sakelar solid-state daripada relay, kamu bisa memakai MOSFET logic-level dan, untuk sirine berbasis kumparan (elektromagnetik), menambahkan dioda terbalik di antara terminalnya.

Notifikasi Telegram dengan ESP32

Sirine yang tidak didengar siapa pun tidak ada gunanya; cara termudah untuk tahu bahwa alarmmu berbunyi ketika kamu sedang pergi adalah ESP32 dan Telegram. ESP32 terhubung ke Wi-Fi-mu dan mengirim pesan ke server Telegram lewat koneksi terenkripsi, dan kamu juga bisa mengirim perintah kepadanya. Cara membuat bot dengan BotFather, mendapatkan token dan chat ID-mu, serta menginstal library UniversalTelegramBot dan ArduinoJson sudah kami jelaskan langkah demi langkah di artikel bot Telegram ESP32. Versi ini tidak punya keypad; kamu mengendalikan sistem dari ponsel dengan perintah /kur (pergi; bahasa Turki untuk “aktifkan”), /evde (mode di rumah; “di rumah”), /coz (menonaktifkan; “lepaskan”), dan /durum (status). Bot hanya menjawab chat ID-mu dan diam-diam mengabaikan perintah dari orang lain.

C++ (Arduino IDE)
1// ESP32 alarm sistemi + Telegram: kurma/çözme komutları ve anlık bildirim / ESP32 alarm + Telegram: arm/disarm commands and instant alerts
2#include <WiFi.h>
3#include <WiFiClientSecure.h>
4#include <UniversalTelegramBot.h>        // ArduinoJson kütüphanesi de gerekir / also needs ArduinoJson
5#include <Preferences.h>                 // kurulu durumu elektrik kesintisinde unutmamak için / remembers the armed state across power cuts
6
7const char* WIFI_SSID = "AgAdi";         // Wi-Fi adın (yalnız 2,4 GHz) / your Wi-Fi name (2.4 GHz only)
8const char* WIFI_PASS = "Sifre123";      // Wi-Fi şifren / your Wi-Fi password
9#define BOT_TOKEN "1234567890:AAExxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"   // BotFather'dan / from BotFather
10#define CHAT_ID   "123456789"            // yalnız bu ID komut verebilir / only this ID may send commands
11
12const int PIR_PIN = 27;                  // HC-SR501 OUT
13const int KAPI_PIN = 33;                 // reed, diğer ucu GND / reed, other leg to GND
14const int TITRESIM_PIN = 32;             // SW-420 DO
15const int BUZZER_PIN = 25;               // aktif buzzer / active buzzer
16const int ROLE_PIN = 26;                 // siren rölesi / siren relay
17const int LED_PIN = 2;                   // dahili LED: kurulu / onboard LED: armed
18const bool ROLE_AKTIF_LOW = true;        // çoğu röle modülü LOW'da çeker / most relay modules switch on with LOW
19
20const unsigned long CIKIS_SURE = 30000;       // çıkış gecikmesi / exit delay
21const unsigned long GIRIS_SURE = 20000;       // giriş gecikmesi / entry delay
22const unsigned long SIREN_SURE = 180000;      // siren en fazla 3 dk / siren for at most 3 min
23const unsigned long PIR_ISINMA = 60000;       // PIR ısınma süresi / PIR warm-up time
24const unsigned long KONTROL_ARALIGI = 2000;   // Telegram'a 2 sn'de bir sor / poll Telegram every 2 s
25
26WiFiClientSecure client;
27UniversalTelegramBot bot(BOT_TOKEN, client);
28Preferences ayar;
29
30enum Durum { COZULU, CIKIS_GECIKMESI, KURULU, GIRIS_GECIKMESI, ALARM };
31const char* const DURUM_ADI[] = { "ÇÖZÜLÜ / DISARMED", "ÇIKIŞ GECİKMESİ / EXIT DELAY", "KURULU / ARMED", "GİRİŞ GECİKMESİ / ENTRY DELAY", "ALARM" };
32
33struct Giris {                           // gürültü süzgeçli giriş / debounced input
34  bool ham;
35  bool kararli;
36  unsigned long degisim;
37};
38Giris gKapi = {false, false, 0};
39Giris gPir = {false, false, 0};
40
41Durum durum = COZULU;
42unsigned long durumZamani = 0;
43bool evdeModu = false;
44bool sirenAcik = false;
45bool pirHazir = false;
46String tetikleyen = "";
47String bekleyen = "";                    // gönderilemeyen bildirimler / notifications not sent yet
48unsigned long sonDeneme = 0;
49unsigned long sonKontrol = 0;
50
51volatile unsigned long darbe[3] = {0, 0, 0};   // son 3 titreşim olayı / last 3 vibration events
52volatile bool zorlama = false;                 // 3 sn'de 3 darbe oldu / 3 hits within 3 s
53
54// Kesme: Telegram beklerken de kısa darbeler kaçmaz / interrupt: short pulses are caught even while Telegram blocks
55void IRAM_ATTR titresimKesmesi() {
56  unsigned long simdi = millis();
57  if (simdi - darbe[2] > 150) {          // 150 ms içindeki sekmeler tek olay / bounces within 150 ms count once
58    darbe[0] = darbe[1];
59    darbe[1] = darbe[2];
60    darbe[2] = simdi;
61    if (darbe[0] != 0 && simdi - darbe[0] <= 3000) zorlama = true;
62  }
63}
64
65bool suz(Giris &g, bool okuma, unsigned long sure) {
66  unsigned long simdi = millis();
67  if (okuma != g.ham) {
68    g.ham = okuma;
69    g.degisim = simdi;
70  }
71  if (simdi - g.degisim >= sure) g.kararli = g.ham;
72  return g.kararli;
73}
74
75void sirenYaz(bool acik) {
76  sirenAcik = acik;
77  if (ROLE_AKTIF_LOW) digitalWrite(ROLE_PIN, acik ? LOW : HIGH);
78  else                digitalWrite(ROLE_PIN, acik ? HIGH : LOW);
79}
80
81void bekleyeniGonder() {
82  sonDeneme = millis();
83  if (WiFi.status() != WL_CONNECTED) {
84    WiFi.reconnect();                    // bağlantı koptuysa yeniden dene / reconnect if the link dropped
85    return;
86  }
87  if (bot.sendMessage(CHAT_ID, bekleyen, "")) bekleyen = "";
88}
89
90void bildir(const String& metin) {       // gönderemezse 15 sn sonra yeniden dener / retries after 15 s if sending fails
91  Serial.println(metin);
92  bekleyen += metin + "\n";
93  if (bekleyen.length() > 1200) bekleyen = bekleyen.substring(bekleyen.length() - 1200);
94  bekleyeniGonder();
95}
96
97void durumDegis(Durum yeni) {
98  durum = yeni;
99  durumZamani = millis();
100  if (yeni == KURULU || yeni == COZULU) {   // yalnız kurma ve çözmede yazılır / written only on arm and disarm
101    ayar.putBool("kurulu", yeni == KURULU);
102    ayar.putBool("evde", evdeModu);
103  }
104}
105
106void alarmVer(const String& bolge) {
107  tetikleyen = bolge;
108  sirenYaz(true);                        // önce siren, sonra mesaj / siren first, message second
109  durumDegis(ALARM);
110  bildir("ALARM! " + bolge);
111}
112
113void kur(bool evde) {
114  if (gKapi.kararli) {
115    bildir("Kapı açık, kurulamadı / door open, not armed");
116    return;
117  }
118  evdeModu = evde;
119  for (int i = 0; i < 3; i++) darbe[i] = 0;
120  zorlama = false;
121  tetikleyen = "";
122  durumDegis(CIKIS_GECIKMESI);
123  bildir(evde ? "Evde modu, 30 sn sonra kurulu (PIR kapalı) / stay mode, armed in 30 s (PIR off)"
124              : "Tam kurulum, 30 sn içinde çık / away mode, leave within 30 s");
125}
126
127void coz() {
128  sirenYaz(false);
129  String m = "Alarm çözüldü / disarmed";
130  if (tetikleyen.length()) m += "\nAlarm hafızası / memory: " + tetikleyen;
131  tetikleyen = "";
132  durumDegis(COZULU);
133  bildir(m);
134}
135
136String durumMetni() {
137  String s = String("Durum / state: ") + DURUM_ADI[durum];
138  if (evdeModu && durum != COZULU) s += " (evde / stay)";
139  s += "\nKapı / door: ";
140  s += gKapi.kararli ? "AÇIK / open" : "kapalı / closed";
141  s += "\nWi-Fi: " + String(WiFi.RSSI()) + " dBm";
142  return s;
143}
144
145void mesajlariIsle(int adet) {
146  for (int i = 0; i < adet; i++) {
147    String chatId = bot.messages[i].chat_id;
148    String metin = bot.messages[i].text;
149    if (chatId != CHAT_ID) continue;     // yabancıya yanıt yok / strangers get no answer
150
151    int at = metin.indexOf('@');         // "/kur@bot_adi" -> "/kur"
152    if (at > 0) metin = metin.substring(0, at);
153
154    if (metin == "/kur" && durum == COZULU)        kur(false);
155    else if (metin == "/evde" && durum == COZULU)  kur(true);
156    else if (metin == "/coz" && durum != COZULU)   coz();
157    else if (metin == "/durum")                    bildir(durumMetni());
158    else bot.sendMessage(chatId, "/kur  /evde  /coz  /durum\n" + durumMetni(), "");
159  }
160}
161
162void setup() {
163  Serial.begin(115200);
164  pinMode(ROLE_PIN, OUTPUT);
165  sirenYaz(false);                       // açılışta siren kapalı / siren off at boot
166  pinMode(BUZZER_PIN, OUTPUT);
167  pinMode(LED_PIN, OUTPUT);
168  pinMode(PIR_PIN, INPUT);
169  pinMode(KAPI_PIN, INPUT_PULLUP);       // kapı kapalı = LOW, açık ya da kablo kesik = HIGH / closed = LOW, open or cut = HIGH
170  pinMode(TITRESIM_PIN, INPUT);
171  attachInterrupt(digitalPinToInterrupt(TITRESIM_PIN), titresimKesmesi, RISING);
172
173  ayar.begin("alarm", false);
174  if (ayar.getBool("kurulu", false)) {   // kesintiden önce kuruluysa kurulu başla / start armed if it was armed before the outage
175    evdeModu = ayar.getBool("evde", false);
176    durumDegis(KURULU);
177  }
178
179  WiFi.mode(WIFI_STA);
180  WiFi.begin(WIFI_SSID, WIFI_PASS);
181  unsigned long bas = millis();
182  while (WiFi.status() != WL_CONNECTED && millis() - bas < 20000) delay(500);   // Wi-Fi yoksa alarm yine çalışır / the alarm works without Wi-Fi too
183  client.setCACert(TELEGRAM_CERTIFICATE_ROOT);   // api.telegram.org kök sertifikası / root certificate of api.telegram.org
184  if (WiFi.status() == WL_CONNECTED) {
185    while (bot.getUpdates(bot.last_message_received + 1)) { }   // kapalıyken gelen eski komutları atla / skip commands sent while off
186  }
187  bildir(String("Sistem açıldı (elektrik geldi mi?) / system started (power restored?)\n") + durumMetni());
188}
189
190void loop() {
191  unsigned long simdi = millis();
192  if (!pirHazir && simdi >= PIR_ISINMA) pirHazir = true;
193  bool kapiAcik = suz(gKapi, digitalRead(KAPI_PIN) == HIGH, 50);
194  bool hareket = suz(gPir, digitalRead(PIR_PIN) == HIGH, 200) && pirHazir;
195  bool titresim = zorlama;
196  zorlama = false;
197
198  switch (durum) {
199    case CIKIS_GECIKMESI:
200      if (simdi - durumZamani >= CIKIS_SURE) {
201        durumDegis(KURULU);
202        bildir("Sistem kuruldu / system armed");
203      }
204      break;
205    case KURULU:
206      if (titresim)                  alarmVer("Pencere (titreşim) / window (vibration)");
207      else if (hareket && !evdeModu) alarmVer("İç mekân (PIR) / interior (PIR)");
208      else if (kapiAcik) {
209        durumDegis(GIRIS_GECIKMESI);
210        bildir("Kapı açıldı, 20 sn giriş süresi / door opened, 20 s entry delay");
211      }
212      break;
213    case GIRIS_GECIKMESI:
214      if (titresim)                                alarmVer("Pencere (titreşim) / window (vibration)");
215      else if (simdi - durumZamani >= GIRIS_SURE)  alarmVer("Giriş kapısı / entry door");
216      break;
217    case ALARM:
218      if (sirenAcik && simdi - durumZamani >= SIREN_SURE) {
219        sirenYaz(false);
220        bildir("Siren sustu, alarm hafızada / siren stopped, alarm kept in memory");
221      }
222      break;
223    default:
224      break;
225  }
226
227  unsigned long gecen = millis() - durumZamani;
228  bool buzzer = false;
229  if (durum == CIKIS_GECIKMESI)      buzzer = (gecen % 1000) < 60;
230  else if (durum == GIRIS_GECIKMESI) buzzer = (gecen % 400) < 200;
231  else if (durum == ALARM)           buzzer = sirenAcik;
232  digitalWrite(BUZZER_PIN, buzzer ? HIGH : LOW);
233  digitalWrite(LED_PIN, (durum == KURULU || durum == ALARM) ? HIGH : LOW);
234
235  if (millis() - sonKontrol > KONTROL_ARALIGI && WiFi.status() == WL_CONNECTED) {
236    int adet = bot.getUpdates(bot.last_message_received + 1);
237    while (adet) {
238      mesajlariIsle(adet);
239      adet = bot.getUpdates(bot.last_message_received + 1);
240    }
241    sonKontrol = millis();
242  }
243  if (bekleyen.length() && millis() - sonDeneme > 15000) bekleyeniGonder();
244}

Kode ESP32 berbeda dalam tiga hal penting. Pertama, mengirim pesan ke Telegram atau mengecek perintah baru bisa makan waktu 1–2 detik, dan selama itu loop() menunggu. Pintu yang tetap terbuka dan PIR yang tetap HIGH setidaknya beberapa detik tidak terpengaruh, tapi pulsa SW-420 yang hanya beberapa milidetik bisa terlewat, jadi getaran ditangkap dengan interrupt (attachInterrupt). Kedua, alarmVer() menyalakan sirine dulu lalu mengirim pesan sesudahnya, jadi selambat apa pun internetnya, sirine tidak pernah tertunda. Notifikasi yang gagal dimasukkan ke antrean dan dicoba lagi setiap 15 detik. Ketiga, library Preferences menyimpan status aktif-tidaknya sistem di memori flash: kalau listrik padam lalu kembali ketika sistem aktif, ESP32 menyala dalam keadaan aktif dan memberi tahu bahwa listrik sudah kembali. Kalau relay dan sirine berbagi catu daya lemah yang sama dengan ESP32, board bisa restart dengan error Brownout detector was triggered; solusinya ada di artikel ESP32 terus restart.

Wi-Fi tidak selalu bisa diandalkan: ketika listrik padam, router juga mati, dan pencuri bisa saja mencabutnya. Memasang router ke UPS kecil, atau mengirim SMS sebagai jalur cadangan dengan modul GSM SIM800L, memastikan notifikasinya tetap sampai apa pun yang terjadi. Kalau kamu juga ingin foto ketika gerakan terdeteksi, lihat mengirim foto ke Telegram dengan ESP32-CAM.

Alarm dengan Raspberry Pi Pico dan MicroPython

State machine yang sama bisa kamu buat di Raspberry Pi Pico dalam program MicroPython yang pendek. Keypad dibaca dengan fungsi pemindaian baris-kolom dari panduan keypad; baris ada di GP2–GP5 dan kolom di GP6–GP9 dengan pull-up internal. Beri daya PIR dari VBUS (5 V); output-nya sudah 3,3 V, jadi bisa langsung disambung ke GP16. Selalu beri daya SW-420 dari pin 3V3(OUT), karena output modul yang diberi 5 V bisa merusak input Pico. Pulsa getaran ditangkap dengan Pin.irq dan waktunya disimpan di sebuah list, sementara state dilacak sebagai string sederhana. Waktu diukur dengan time.ticks_diff(), karena fungsi ini memberi selisih yang benar bahkan ketika timer berputar kembali ke nol.

MicroPython
1# Raspberry Pi Pico alarm: keypad + PIR + kapı + titreşim, durum makinesi / Pico alarm: keypad + PIR + door + vibration, state machine
2from machine import Pin
3import time
4
5SIFRE = "2580"                            # kendi şifreni yaz / set your own code
6CIKIS_MS, GIRIS_MS, SIREN_MS = 30000, 20000, 180000
7
8KEYS = ["123A", "456B", "789C", "*0#D"]
9rows = [Pin(n, Pin.OUT, value=1) for n in (2, 3, 4, 5)]       # R1–R4
10cols = [Pin(n, Pin.IN, Pin.PULL_UP) for n in (6, 7, 8, 9)]    # C1–C4
11pir = Pin(16, Pin.IN)                     # HC-SR501 OUT (3,3 V)
12kapi = Pin(15, Pin.IN, Pin.PULL_UP)       # reed, diğer ucu GND / reed, other leg to GND
13titresim = Pin(17, Pin.IN)                # SW-420 DO, modül 3V3'ten / module powered from 3V3
14buzzer = Pin(14, Pin.OUT, value=0)        # aktif buzzer / active buzzer
15role = Pin(13, Pin.OUT, value=1)          # aktif LOW röle: 1 = kapalı / active-LOW relay: 1 = off
16led = Pin("LED", Pin.OUT)                 # dahili LED: kurulu / onboard LED: armed
17
18darbeler = []                             # son titreşim zamanları / recent vibration times
19son_darbe = 0
20
21
22def titresim_irq(p):
23    global son_darbe
24    simdi = time.ticks_ms()
25    if time.ticks_diff(simdi, son_darbe) > 150:     # sekmeler tek olay / bounces count once
26        son_darbe = simdi
27        darbeler.append(simdi)
28
29
30titresim.irq(trigger=Pin.IRQ_RISING, handler=titresim_irq)
31
32
33def tus_oku():
34    for r, row in enumerate(rows):
35        row.value(0)                      # bu satırı etkinleştir / activate this row
36        for c, col in enumerate(cols):
37            if col.value() == 0:
38                row.value(1)
39                return KEYS[r][c]
40        row.value(1)
41    return None
42
43
44durum = "COZULU"
45zaman = time.ticks_ms()
46acilis = time.ticks_ms()
47pir_hazir = False
48evde = False
49girilen = ""
50son_tus = None
51
52
53def degis(yeni):
54    global durum, zaman
55    durum, zaman = yeni, time.ticks_ms()
56    print("Durum / state:", yeni)
57
58
59def alarm(bolge):
60    role.value(0)                         # siren açık / siren on
61    degis("ALARM")
62    print("ALARM! Bolge / zone:", bolge)
63
64
65while True:
66    tus = tus_oku()
67    if tus != son_tus:                    # değişimi 20 ms sonra doğrula / confirm the change after 20 ms
68        time.sleep_ms(20)
69        if tus_oku() != tus:
70            tus = son_tus                 # sekme, yok say / contact bounce, ignore it
71    if tus and tus != son_tus:            # her basış bir kez / each press once
72        if tus in "0123456789":
73            girilen = (girilen + tus)[-8:]
74        elif tus in "*C":
75            girilen = ""
76        else:
77            dogru = girilen == SIFRE
78            girilen = ""
79            if not dogru:
80                print("Yanlis sifre / wrong code")
81            elif durum == "COZULU" and tus in "AB":
82                if kapi.value():
83                    print("Kapi acik, kurulamaz / door open, cannot arm")
84                else:
85                    evde = tus == "B"     # B = evde modu, PIR kapalı / B = stay mode, PIR off
86                    darbeler.clear()
87                    degis("CIKIS")
88            elif durum != "COZULU" and tus == "#":
89                role.value(1)
90                degis("COZULU")
91    son_tus = tus
92
93    simdi = time.ticks_ms()
94    gecen = time.ticks_diff(simdi, zaman)
95    if not pir_hazir and time.ticks_diff(simdi, acilis) > 60000:
96        pir_hazir = True                  # PIR ısındı / PIR warmed up
97    kapi_acik = kapi.value() == 1
98    hareket = pir_hazir and pir.value() == 1 and not evde
99    darbeler[:] = [t for t in darbeler if time.ticks_diff(simdi, t) < 3000]
100    zorlama = len(darbeler) >= 3          # 3 sn'de 3 darbe / 3 hits in 3 s
101
102    if durum == "CIKIS" and gecen >= CIKIS_MS:
103        degis("KURULU")
104    elif durum == "KURULU":
105        if zorlama:
106            alarm("Pencere / window")
107        elif hareket:
108            alarm("PIR")
109        elif kapi_acik:
110            degis("GIRIS")
111    elif durum == "GIRIS":
112        if zorlama:
113            alarm("Pencere / window")
114        elif gecen >= GIRIS_MS:
115            alarm("Kapi / door")
116    elif durum == "ALARM" and role.value() == 0 and gecen >= SIREN_MS:
117        role.value(1)                     # siren susar, hafıza kalır / siren stops, memory stays
118
119    gecen = time.ticks_diff(time.ticks_ms(), zaman)
120    if durum == "CIKIS":
121        buzzer.value(1 if gecen % 1000 < 60 else 0)
122    elif durum == "GIRIS":
123        buzzer.value(1 if gecen % 400 < 200 else 0)
124    else:
125        buzzer.value(1 if durum == "ALARM" and role.value() == 0 else 0)
126    led.value(1 if durum in ("KURULU", "ALARM") else 0)
127    time.sleep_ms(10)

Cara mengurangi alarm palsu

Musuh terbesar sistem alarm bukan pencuri, melainkan alarm palsu: setelah sirine berbunyi tanpa alasan tiga kali, kamu maupun tetanggamu tidak akan menganggapnya serius lagi. Sebagian langkah di bawah sudah ada di kode; sisanya soal pemasangan dan pengkabelan:

  • Tunggu pemanasan: PIR bisa terpicu sendiri di 30–60 detik pertama setelah dinyalakan; kode mengabaikan 60 detik pertama.
  • Pakai filter: Menunggu setiap input tetap stabil selama waktu tertentu (debounce) menyaring gangguan singkat di kabel.
  • Hitung ketukan: Mencari 3 ketukan dalam 3 detik, bukan satu ketukan saja, menyaring angin, truk yang lewat, dan pintu yang terbanting. Kalau perlu, naikkan hitungannya ke 4–5 atau turunkan sensitivitas dengan trimpot di board.
  • Pasang PIR dengan cermat: Jangan arahkan ke pemanas ruangan, AC, jendela yang kena matahari, atau gorden; sensor ini mendeteksi paling baik ketika gerakan melintasi bidang pandangnya dari sisi ke sisi. Kalau kamu punya hewan peliharaan, pasang sensor di tempat tinggi, miringkan sedikit ke bawah, dan turunkan sensitivitasnya.
  • Wajibkan dua sensor: Di area kritis, mensyaratkan PIR dan laser, atau PIR dan pintu, terpicu dalam selang beberapa detik (cross-zoning) mencegah satu sensor rusak menyebabkan alarm.
  • Bersihkan catu daya: Penurunan tegangan ketika relay menyala bisa memicu PIR. Pasang kapasitor 100 µF dan 100 nF di antara VCC dan GND setiap sensor, dan pisahkan jalur kabel relay dan sirine dari kabel sensor.
  • Pakai pull-up eksternal di kabel panjang: Pull-up internal Arduino bernilai 20–50 kΩ dan menangkap derau di kabel 10 meter; resistor eksternal 4,7 kΩ ditambah kapasitor 100 nF di pin membuat jalurnya jauh lebih tangguh.
  • Lindungi laser dari cahaya sekitar: Masukkan LDR ke dalam tabung hitam dan uji ambangnya secara terpisah di siang hari dan malam hari.
Sebelum mengandalkan sistemnya, lakukan uji jalan (walk test): lepaskan sirine dari terminal COM relay, aktifkan alarm, lalu jalani rute yang akan diambil pencuri di rumahmu. Perhatikan di Serial Monitor zona mana yang terpicu dan kapan, lalu jalankan beberapa hari hanya dengan buzzer untuk mengecek apakah ada pemicuan sendiri.

Kesalahan umum dan solusinya

  • Alarm berbunyi begitu diaktifkan: PIR masih dalam pemanasan atau kontak pintu salah sambung. Di tes sensor, pintu seharusnya terbaca LOW ketika tertutup; kalau terbaca HIGH, magnetnya terlalu jauh atau ada kabel yang putus.
  • Keypad tidak merespons atau membaca tombol yang salah: Kabel baris dan kolom tertukar. Alih-alih menyambung ulang, ubah urutan array satirPinleri (pin baris) dan sutunPinleri (pin kolom) di kode.
  • Kamu mendengar bip panjang ketika mencoba mengaktifkan: Itu bukan kerusakan, melainkan peringatan belum siap: pintu terbuka atau sinar laser tidak sampai ke LDR. Tekan D untuk mengecek zona.
  • Relay klik sebentar ketika dinyalakan: Level mati tidak ditulis sebelum pin dijadikan output, atau ROLE_AKTIF_LOW dibiarkan true pada relay aktif HIGH.
  • Arduino reset ketika sirine berbunyi: Sirine dan Arduino berbagi adaptor yang lemah dan penurunan tegangannya me-reboot board. Pakai adaptor yang lebih kuat, konverter terpisah, dan kabel yang lebih tebal; detailnya di Arduino terus reset.
  • Sensor getaran tidak pernah terpicu atau terpicu terus: Putar trimpot di board pelan-pelan untuk mengatur ambang, lalu tempelkan sensor dengan kuat ke kaca atau kusen; sensor yang longgar tidak meneruskan ketukannya.
  • ESP32 tidak mengirim pesan: Token atau chat ID salah, jaringannya 5 GHz, atau kamu belum pernah memulai bot di Telegram. Bot tidak bisa mengirimimu pesan sampai kamu mengirim /start kepadanya sekali.
  • Laser berfungsi baik di siang hari tapi memberi alarm palsu di malam hari (atau sebaliknya): Ambangnya terlalu dekat dengan tingkat cahaya sekitar. Ukur nilai dengan sinar dan tanpa sinar di kedua kondisi, lalu pilih LAZER_ESIK baru di tengahnya.

Ide untuk mengembangkan sistem alarmmu

  • Aktifkan dan nonaktifkan dengan kartu: Tambahkan pembaca RFID RC522 di sebelah keypad dan nonaktifkan alarm dengan gantungan kunci RFID.
  • Zona 24 jam: Tambahkan sensor gas MQ-2 atau sensor api untuk membuat zona gas dan kebakaran yang membunyikan alarm bahkan ketika sistem nonaktif.
  • Sensor pintu nirkabel: Buat kontak pintu bertenaga baterai untuk jendela yang jauh dengan modul RF 433 MHz.
  • Panel dengan layar: Tampilkan hitung mundur, status, dan memori alarm di LCD I2C.
  • Integrasi rumah pintar: Hubungkan ESP32 ke MQTT dan Home Assistant dan kelola alarm bersama lampu dan kameramu.
  • Kontak tamper: Pasang micro switch di tutup kotak sehingga buzzer berbunyi ketika kotaknya dibuka, bahkan ketika sistem nonaktif.
  • Proyek maket rumah: Gabungkan semua zona ini di maket rumah sebagai lapisan keamanan proyek rumah pintar.

Karena menggabungkan pembacaan sensor, pengaturan waktu, state machine, dan desain catu daya dalam satu proyek, sistem alarm Arduino juga cara belajar yang sangat bagus. Jalankan tes sensor dulu, lalu coba alarmnya hanya dengan buzzer selama beberapa hari, dan sambungkan sirine paling akhir. Di aplikasi BlueGrays kamu akan menemukan proyek seperti Sensor Pintu dengan Sakelar Reed, Sistem Keamanan Laser, Kunci Sandi dengan Keypad 4x4 dan LCD, serta Sistem Alarm Gerakan ESP32 dengan notifikasi Telegram, masing-masing dengan skema rangkaian dan kode langkah demi langkah.

Pertanyaan umum

Bagaimana cara membuat sistem alarm dengan Arduino?

Kamu menyambungkan sensor gerak PIR, kontak reed untuk pintu, keypad, buzzer aktif, dan modul relay yang menggerakkan sirine ke Arduino Uno. Kodenya ditulis sebagai state machine yang memindahkan sistem antara state nonaktif, jeda keluar, aktif, jeda masuk, dan alarm. Kamu mengaktifkan dan menonaktifkannya dengan kode di keypad, dan kalau ada sensor yang terpicu ketika sistem aktif, sirine berbunyi.

Sensor apa saja yang dipakai di sistem alarm Arduino?

Yang paling umum adalah sensor gerak PIR (HC-SR501) untuk area dalam ruangan, kontak reed (MC-38) untuk pintu dan jendela, sensor getaran SW-420 untuk ketukan di kaca, dan tripwire laser untuk lorong. Kamu bisa menambahkan MQ-2 untuk gas dan sensor api untuk kebakaran. Setiap sensor didefinisikan sebagai zona terpisah dengan perilakunya sendiri.

Apakah alarm Arduino tetap bekerja ketika listrik padam?

Tidak, kalau hanya diberi daya dari adaptor. Dengan catu daya alarm 13,8 V yang mengisi aki 12 V 7 Ah, sistem Uno yang menarik sekitar 100 mA bisa jalan selama 4–5 hari. Di versi ESP32, status aktif disimpan di flash, jadi sistem kembali dalam keadaan aktif ketika listrik menyala lagi; router juga butuh UPS supaya notifikasi bisa terkirim.

Bisakah alarm Arduino mengirim notifikasi ke ponselku?

Arduino Uno tidak bisa melakukannya sendiri, tapi ESP32 bisa mengirim pesan Telegram secara instan lewat Wi-Fi dan menerima perintah seperti /kur dan /coz dari ponselmu. Di tempat tanpa internet, modul GSM SIM800L bisa mengirim SMS atau menelepon. Memakai kedua cara sekaligus adalah pilihan yang paling andal.

Kenapa sensor PIR-ku memberi alarm palsu?

Penyebab yang biasa adalah pemanasan 30–60 detik setelah dinyalakan, sumber panas seperti pemanas ruangan dan AC, gorden yang bergoyang di bawah sinar matahari yang hangat, dan derau catu daya ketika relay menyala. Mengabaikan masa pemanasan di kode, menjauhkan sensor dari sumber panas, dan menambahkan kapasitor di antara VCC dan GND menyelesaikan sebagian besar masalah ini.

Bisakah sistem alarm Arduino dipakai di rumah sungguhan?

Sistem ini bagus untuk belajar dan untuk memantau kamar, garasi, atau gudang, tapi tidak menggantikan sistem alarm bersertifikat. Sistem komersial diuji sesuai standar seperti EN 50131 untuk perlindungan tamper, daya cadangan, koneksi nirkabel terenkripsi, dan sambungan ke pusat pemantauan. Kalau kamu memakai sistem Arduino di rumah, kami sarankan menambahkan aki cadangan, kontak tamper, dan jalur notifikasi kedua.

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
Sistem Keamanan Laser
#23Mahir
Premium

Sistem Keamanan Laser

Persis seperti adegan perampokan dengan laser di film: sinar laser menyorot sensor cahaya, dan begitu ada orang masuk dan memutus sinarnya, LED merah menyala dan buzzer mulai berbunyi. Alarmnya tidak

4.8(60)
77
Sistem Peringatan Laser
#22Menengah
Premium

Sistem Peringatan Laser

Kamu memasang pagar sinar tak terlihat, persis seperti di film. Laser menyorot sensor cahaya di sisi seberang; begitu ada orang lewat dan memutus sinarnya, LED menyala dan buzzer berbunyi, dan ketika

4.8(64)
83
Sensor Pintu dengan Sakelar Reed
#38Pemula
Premium

Sensor Pintu dengan Sakelar Reed

Ketahui apakah pintumu terbuka atau tertutup hanya dengan melihat lampu. Kamu menempelkan magnet kecil di pintu dan sakelar peka magnet di kusennya; ketika pintu tertutup keduanya bersebelahan dan LED

4.7(34)
44
Kunci Sandi dengan Keypad 4x4 dan LCD
#64Mahir
Premium

Kunci Sandi dengan Keypad 4x4 dan LCD

Kunci pintu elektronik kecil yang menahan siapa pun yang tidak tahu kodenya. Kamu menekan tombol pagar lalu mengetik kodenya di keypad; jika benar, layar menulis akses diberikan, LED hijau menyala, da

4.8(59)
75
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
Brankas Elektronik Pintar
#92Mahir
Premium

Brankas Elektronik Pintar

Brankas kecil untuk barang berhargamu yang terbuka dengan kode. Kamu mengetik kode empat digitmu di keypad; jika benar, motor servo membuka kunci, lampu hijau menyala, dan layar menampilkan keadaan br

4.7(42)
63