Arduino Alarmanlage bauen: PIR, Türkontakt, Keypad und Sirene
Arduino Alarmanlage selber bauen: PIR, Reedkontakt, Erschütterungs- und Lasermelder, Keypad-PIN, Ein- und Ausgangsverzögerung, Sirene und ESP32-Telegram.
Eine Arduino Alarmanlage ist eine Sicherheitstechnik zum Selberbauen: Sie schaltet eine Sirene ein, wenn jemand unbefugt deine Wohnung oder dein Zimmer betritt, gibt dir beim Öffnen der Tür ein paar Sekunden für deine PIN und schickt dir auf Wunsch sofort eine Nachricht aufs Handy. In dieser Anleitung baust du eine Anlage mit vier Zonen: einen PIR-Bewegungsmelder, einen Reedkontakt an der Tür, einen Erschütterungssensor, der Schläge auf die Fensterscheibe erkennt, und eine Laser-Lichtschranke quer durch den Flur. Du schaltest das System per PIN am 4x4-Keypad scharf und unscharf, steuerst eine 12-V-Sirene über ein Relais und erledigst alles mit einem einzigen Arduino Uno.
Schritt für Schritt bauen wir eine kleine Kopie dessen, was kommerzielle Alarmzentralen können: Ein- und Ausgangsverzögerung, Intern-scharf (Nachtmodus), Sperre nach falschen PINs, begrenzte Sirenendauer und Alarmspeicher. Den Code schreiben wir nicht als verschachtelte if-Blöcke, sondern als Zustandsautomaten; so ist jederzeit klar, was die Anlage gerade tut. Danach überträgst du dieselbe Logik auf den ESP32, um per Telegram scharf und unscharf zu schalten und Alarmmeldungen zu bekommen, schreibst eine MicroPython-Version für den Raspberry Pi Pico und lernst, wie du das lästigste Problem überhaupt in den Griff bekommst: Fehlalarme.

Wie funktioniert eine Arduino Alarmanlage?
Jede Alarmanlage besteht aus drei Teilen: Eingängen (Sensoren), einem Gehirn, das entscheidet (dem Arduino), und Ausgängen (Sirene, Summer, Benachrichtigung). Jeder Sensor überwacht eine Zone. Professionelle Zentralen teilen Zonen danach ein, wie sie beim Auslösen reagieren, und genau diese Unterscheidung trennt eine gute Alarmanlage von einer nervigen Bastelei, die bei jedem Türöffnen losheult. Unser System hat vier Zonen:
| Zone | Sensor | Zonentyp | Wenn ausgelöst im Scharfzustand |
|---|---|---|---|
| Eingangstür | Reedkontakt (MC-38) | Verzögert | 20 s Eingangsverzögerung; Alarm, wenn keine PIN kommt |
| Wohnzimmer, innen | PIR (HC-SR501) | Innenzone (Mitläufer) | Sofortalarm; wartet, solange die Eingangsverzögerung läuft; intern-scharf aus |
| Fenster | Erschütterung (SW-420) | Sofort | Sofortalarm bei 3 Schlägen innerhalb von 3 s |
| Flur, Treppe | Laser + LDR (KY-008) | Innenzone (Mitläufer) | Sofortalarm; wartet, solange die Eingangsverzögerung läuft |
| Gas, Feuer, Sabotage | MQ-2, Flammensensor, Deckelkontakt | 24 Stunden | Alarm auch im unscharfen Zustand (Erweiterungsidee) |
Dank der verzögerten Zone heult die Sirene nicht sofort los, wenn du nach Hause kommst: Die Tür geht auf, der Summer piept zur Warnung und du hast 20 Sekunden für deine PIN. Eine Mitläufer-Innenzone bleibt geduldig, solange die Eingangsverzögerung läuft; gehst du auf dem Weg von der Tür zum Keypad am PIR vorbei, gibt es keinen Alarm. Kommt aber jemand herein, ohne die Tür zu öffnen, etwa durchs Fenster, und läuft vor den PIR, wurde keine Eingangsverzögerung gestartet und der Alarm geht sofort los. Eine Sofortzone löst immer direkt aus; wer ein Fenster aufhebelt, bekommt keine 20 Sekunden geschenkt. Beim Verlassen funktioniert es ähnlich: Nach dem Scharfschalten startet eine Ausgangsverzögerung von 30 Sekunden, in der du durch die Tür gehen kannst, ohne etwas auszulösen.
Teileliste für die Arduino Alarmanlage
| Bauteil | Anzahl | Aufgabe |
|---|---|---|
| Arduino Uno (oder Nano) | 1 | Das Gehirn des Systems; die Pins dieser Anleitung sind bei Uno und Nano gleich |
| HC-SR501 PIR-Bewegungsmelder | 1 | Bewegungserkennung innen, 3–7 m, etwa 110° |
| MC-38 Magnetkontakt (oder Reedschalter + Magnet) | 1 | Öffnet den Stromkreis, wenn Tür oder Fenster aufgehen |
| SW-420 Erschütterungssensor-Modul | 1 | Erkennt Schläge auf Glas und Rahmen |
| KY-008 Lasermodul + LDR + 10-kΩ-Widerstand | 1 Satz | Lichtschranke quer durch den Flur |
| 4x4-Folientastatur (Keypad) | 1 | Scharf- und Unscharfschalten per PIN |
| Aktiver Summer (5 V) | 1 | Tastenton, Ein- und Ausgangswarnung, Innensirene |
| 1-Kanal-Relaismodul 5 V | 1 | Schaltet die 12-V-Sirene galvanisch getrennt vom Arduino |
| 12-V-Sirene (am besten mit Blitzleuchte) | 1 | Der eigentliche Alarmton, meist 100–120 dB |
| Rote und grüne LED + 2 × 220-Ω-Widerstand | 1 Satz | Anzeige für scharf und bereit |
| 12-V-Netzteil, LM2596-Modul, 12-V-Akku (optional) | 1 Satz | Versorgung und Notstrom bei Stromausfall |
| Breadboard, Jumperkabel, 4-adriges Alarmkabel | - | Verbindungen und entfernte Melder |







Die Melder: Was erkennt welche Zone?
- PIR (HC-SR501): Erkennt Änderungen der Infrarotstrahlung, die der menschliche Körper abgibt, und sieht Bewegungen in 3–7 Metern Entfernung in einem Winkel von etwa 110 Grad. Der Ausgang liefert 3,3 V, die Versorgung muss aber 5 V betragen. In den ersten 30–60 Sekunden nach dem Einschalten lernt er den Raum kennen und kann von selbst auslösen; deshalb ignoriert der Code die ersten 60 Sekunden. Mehr dazu im Artikel PIR-Bewegungsmelder.
- Magnet-Türkontakt (MC-38): Zwei Kunststoffgehäuse, eines am Rahmen, eines am Türflügel festgeschraubt. Bei geschlossener Tür ist der Magnet nah und der Kontakt geschlossen; öffnet sich die Tür um mehr als 15–25 mm, öffnet der Kontakt. Er wird mit einem einzigen Pin und dem internen Pull-up gelesen; was darin steckt, erklären wir im Artikel Reedkontakt.
- Erschütterungssensor (SW-420): Ein Öffner, in dem ein Metallstift von einer Feder umgeben ist. Bekommt Glas oder Rahmen einen Schlag, öffnet der Kontakt kurz, und der LM393-Komparator auf der Platine macht daraus einen digitalen Impuls. Bei den meisten Modulen ist der Ausgang in Ruhe LOW und bei einem Schlag kurz HIGH; prüfe dein Modul mit dem Sensortest. Die Empfindlichkeit stellst du am Poti ein; Details im Artikel SW-420 Erschütterungssensor.
- Laser-Lichtschranke (KY-008 + LDR): Ein 650-nm-Laser mit etwa 5 mW leuchtet ständig auf einen LDR auf der anderen Seite des Flurs. Wird der Strahl unterbrochen, steigt der Widerstand des LDR von einigen hundert Ohm auf mehrere zehn Kiloohm und der Analogwert fällt deutlich. Tipps zu Aufbau und Ausrichtung findest du im Artikel Lasermodul.
- 4x4-Keypad: Eine Matrix mit 16 Tasten aus 4 Zeilen- und 4 Spaltenleitungen; sie belegt 8 Pins und wird mit der Keypad-Bibliothek in einer Zeile ausgelesen. Wie das Zeilen-Spalten-Scannen funktioniert, steht in der Keypad-Anleitung.
- Sirene und Summer: Ein aktiver Summer hat einen eigenen Oszillator und piept, sobald der Pin HIGH ist; ideal für Tastentöne und Ein- und Ausgangswarnungen. Der eigentlich abschreckende Ton kommt von der 12-V-Sirene, die du über ein Relaismodul schaltest. Den Unterschied zwischen aktivem und passivem Summer erklärt der Artikel Was ist ein Buzzer.
Schaltplan und Pinbelegung
Die Tabelle zeigt die Verdrahtung aller drei Boards. Die Arduino-Uno-Version nutzt alle vier Zonen und das Keypad. Weil die ESP32-Version statt mit Keypad per Telegram-Befehl geschaltet wird und die Pico-Version kurz gehalten ist, gibt es die Laser-Lichtschranke nur am Uno. Die Pinnummern deines Boards siehst du im Werkzeug Pinout, LED-Vorwiderstände berechnest du mit dem LED-Vorwiderstandsrechner.
| Verbindung | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| Keypad R1–R4 (Zeilen) | D9, D8, D7, D6 | – (Telegram) | GP2, GP3, GP4, GP5 |
| Keypad C1–C4 (Spalten) | D5, D4, D3, D2 | – (Telegram) | GP6, GP7, GP8, GP9 |
| PIR VCC / OUT / GND | 5V / D10 / GND | VIN (5V) / GPIO 27 / GND | VBUS / GP16 / GND |
| Reedkontakt (2 Adern) | D11 und GND | GPIO 33 und GND | GP15 und GND |
| SW-420 VCC / DO / GND | 5V / D12 / GND | 3V3 / GPIO 32 / GND | 3V3(OUT) / GP17 / GND |
| KY-008 S / − (Laser dauerhaft an) | 5V / GND | – | – |
| LDR zwischen 5V und A0, 10 kΩ zwischen A0 und GND | A0 | – | – |
| Aktiver Summer + (− an GND) | A1 | GPIO 25 | GP14 |
| Relaismodul IN | A2 | GPIO 26 | GP13 |
| Grüne LED (mit 220 Ω): bereit | A3 | – | – |
| Rote LED (mit 220 Ω): scharf | D13 | GPIO 2 (Onboard-LED) | Onboard-LED |
Eine Alarmanlage ist bei Stromausfall am wichtigsten, also plane die Versorgung von Anfang an. Ein praktischer Aufbau: Wie bei professionellen Zentralen versorgt ein 13,8-V-Alarmnetzteil mit Akkuanschluss die Sirene und, mit einem LM2596-Abwärtswandler auf 5 V gebracht, den 5V-Pin des Arduino. Fällt der Strom aus, übernimmt ein 12-V-Blei-Gel-Akku mit 7 Ah. Die Rechnung ist einfach: Uno, PIR, Laser, LEDs und SW-420 ziehen im Ruhezustand etwa 100 mA bei 5 V; das sind 0,5 W, mit einem Wandler mit 85 % Wirkungsgrad also rund 50 mA auf der Akkuseite. Selbst wenn du nur 80 % der Kapazität nutzt, hält ein 7-Ah-Akku das System 4–5 Tage am Laufen; die 0,2–0,5 A der Sirene im Alarmfall verkürzen das etwas. Deine eigenen Werte probierst du mit dem Akkulaufzeit-Rechner aus, Versorgungsmöglichkeiten erklärt Arduino richtig mit Strom versorgen.
Schritt-für-Schritt-Aufbau
- Öffne in der Arduino IDE den Bibliotheksverwalter, suche nach Keypad und installiere die Bibliothek von Mark Stanley und Alexander Brevig. Fehlt sie, bekommst du beim Kompilieren den Fehler Keypad.h: No such file or directory.
- Halte das Keypad mit den Tasten zu dir; verbinde die ersten vier Adern von links (R1–R4) mit D9, D8, D7, D6 und die nächsten vier (C1–C4) mit D5, D4, D3, D2.
- Schließe den PIR an 5V, D10 und GND an; dreh beide Potis ganz gegen den Uhrzeigersinn und setze den Trigger-Jumper auf H.
- Verbinde die beiden Adern des Reedkontakts mit D11 und GND, den SW-420 mit 5V, D12 und GND.
- Setze den LDR zwischen 5V und A0 und den 10-kΩ-Widerstand zwischen A0 und GND; steck den LDR ans Ende eines schwarzen Röhrchens und richte den Laser genau auf dessen Mitte aus.
- Schließe den Summer an A1 an, den IN-Pin des Relaismoduls an A2 und dessen VCC und GND an 5V und GND des Arduino; die LEDs kommen über 220-Ω-Widerstände an A3 und D13.
- Lade zuerst den Sensortest unten hoch, öffne den seriellen Monitor mit 9600 Baud und löse jeden Sensor einzeln aus.
- Lade danach den vollständigen Alarm-Sketch hoch, ändere die PIN und teste das System ein paar Tage nur mit dem Summer, bevor du die Sirene anschließt.
Erster Test: alle Sensoren im seriellen Monitor
Bevor es an den Alarmcode geht, prüfe, ob jeder Sensor sich so verhält wie erwartet. Dieser kleine Sketch gibt zweimal pro Sekunde den PIR-Zustand aus, ob die Tür offen oder zu ist, wie viele Erschütterungsimpulse in der letzten halben Sekunde kamen und den Analogwert des LDR. Der Erschütterungspin wird ständig gelesen, weil die Impulse des SW-420 nur wenige Millisekunden dauern können und Code mit delay() dazwischen sie verpassen würde.
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}Zustandsautomat: das Gehirn der Alarmanlage
Wer eine Alarmanlage mit if-Blöcken und delay() schreibt, landet schnell in einem undurchschaubaren Knäuel: Während auf die Eingangsverzögerung gewartet wird, liest niemand das Keypad, und während die Sirene läuft, kann man keine PIN eingeben. Die Lösung ist ein Zustandsautomat (State Machine), bei dem das System zu jedem Zeitpunkt in genau einem Zustand ist und nur bei bestimmten Ereignissen wechselt. Im Code werden die Zustände mit einem enum definiert, loop() wendet bei jedem Durchlauf nur die Regeln des aktuellen Zustands an, und Wartezeiten werden mit millis() statt delay() gemessen. So läuft die Schleife tausende Male pro Sekunde und kein Tastendruck und kein Sensorereignis geht verloren.
| Zustand | Wann betreten? | Was passiert? | Wann verlassen? |
|---|---|---|---|
| UNSCHARF | Beim Start und nach PIN + # | Beobachtet die Sensoren, löst aber nie aus; grüne LED leuchtet, wenn alles bereit ist | PIN + A oder PIN + B |
| AUSGANGSVERZÖGERUNG | PIN + A (extern) oder B (intern) | Zählt 30 s herunter; Summer piept jede Sekunde, in den letzten 10 s schneller | Nach Ablauf SCHARF; mit PIN + # UNSCHARF |
| SCHARF | Nach Ablauf der Ausgangsverzögerung | Überwacht alle Zonen; rote LED leuchtet dauerhaft | Tür auf: EINGANG; Sofort- oder Innenzone: ALARM |
| EINGANGSVERZÖGERUNG | Tür geöffnet im Scharfzustand | Summer piept 20 s lang; PIR und Laser warten | Mit PIN + # UNSCHARF; nach Ablauf ALARM |
| ALARM | Eine Zone hat ausgelöst | Sirene und Summer an; Sirene stoppt nach 3 min, rote LED blinkt schnell | Nur mit PIN + # UNSCHARF |
Scharf- und Unscharfschalten mit PIN am Keypad
| Tasten | Funktion |
|---|---|
| 2580 + A | Extern-scharf: alle vier Zonen aktiv, 30 s Ausgangsverzögerung |
| 2580 + B | Intern-scharf (Nachtmodus): PIR aus; Tür, Fenster und Laser aktiv |
| 2580 + # | Unscharf schalten; nach einem Alarm wird die auslösende Zone im seriellen Monitor angezeigt |
| * oder C | Falsch eingetippte PIN löschen |
| D | Zonentest: gibt Tür-, PIR- und Laserwerte im seriellen Monitor aus |
| 3 falsche PINs | Keypad wird 60 s gesperrt; in dieser Zeit wird auch die richtige PIN abgelehnt |
Dass auch zum Scharfschalten die PIN nötig ist, verhindert, dass Kinder oder Gäste das System versehentlich aktivieren. Der Code macht außerdem eine Nicht-bereit-Prüfung: Versuchst du bei offener Tür oder unterbrochenem Laserstrahl scharf zu schalten, hörst du einen langen Piepton und nichts passiert, genau wie bei kommerziellen Zentralen. Lass die PIN nicht auf 2580 und vermeide leicht zu ratende Codes wie 1234, 0000 oder dein Geburtsjahr. Möchtest du die PIN ändern, ohne neu hochzuladen, speichere sie im EEPROM und ergänze ein Menü, in dem du am Keypad eine neue PIN eingibst. Mit einem I2C-LCD neben dem Keypad siehst du auch den Countdown und Statusmeldungen; deshalb sind A4 und A5 frei geblieben.
Code der Arduino Alarmanlage
Der folgende Code ist das Herzstück dieser Anleitung. Im Einstellungsbereich ganz oben änderst du PIN, Verzögerungen, Sirenendauer, Laserschwelle und Relaistyp; den Rest musst du nicht anfassen. Auf einem Arduino Uno belegt er etwa 6 KB Programmspeicher und 620 Byte RAM, es bleibt also reichlich Platz für ein LCD oder einen RFID-Leser.
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}Das Rückgrat des Codes ist die Funktion suz(). Ändert sich der Rohwert eines Eingangs, wird ein Zeitstempel genommen, und der stabile Zustand ändert sich erst, wenn der Wert eine bestimmte Zeit gleich bleibt (50 ms für die Tür, 200 ms für den PIR, 30 ms für den Laser). Dieser einfache Filter verhindert, dass kurze Störimpulse auf langen Leitungen und Kontaktprellen einen Alarm auslösen. Auch das Lesen des Türpins mit INPUT_PULLUP ist Absicht: Bei geschlossener Tür zieht der Reedkontakt den Pin auf GND und er liest LOW; öffnet sich die Tür oder durchtrennt jemand das Kabel, wird der Pin HIGH. Diese Ruhestromlogik echter Alarmanlagen wertet ein durchgeschnittenes Kabel genauso wie eine geöffnete Tür.
Für die Erschütterung kommt ein anderes Verfahren zum Einsatz. Schon ein einzelner Schlag lässt den SW-420 innerhalb weniger Millisekunden mehrmals prellen; titresimVar() zählt zuerst alles innerhalb von 150 ms als ein Ereignis und merkt sich dann die Zeiten der letzten drei Ereignisse. Passen drei Ereignisse in 3 Sekunden, gilt das als echter Aufbruchversuch; Wind, der an der Tür rüttelt, oder ein einzelner Ball gegen die Scheibe lösen nichts aus. Die Sirene verstummt nach 3 Minuten von selbst, das System bleibt aber im ALARM-Zustand und die rote LED blinkt schnell, damit jeder, der heimkommt, den Alarm bemerkt. Gibst du die richtige PIN ein, erscheint der in der Variablen tetikleyen gespeicherte Zonenname im seriellen Monitor; eine kleine Version des Alarmspeichers kommerzieller Zentralen.
Ein- und Ausgangsverzögerung richtig einstellen
Sind die Verzögerungen zu kurz, löst du deinen eigenen Alarm aus; sind sie zu lang, schenkst du einem Einbrecher Zeit. Wähle die Ausgangsverzögerung doppelt so lang wie den Weg vom Keypad zur Tür samt Abschließen und die Eingangsverzögerung etwa doppelt so lang wie Tür öffnen, zum Keypad gehen und PIN eintippen. Kommerzielle Zentralen nutzen meist 30–60 Sekunden zum Verlassen und 15–30 Sekunden zum Betreten. Hängst du das Keypad direkt neben die Tür, aber von außen nicht sichtbar, kannst du die Eingangsverzögerung kurz halten. Auch das Verhalten des Summers zählt: Beim Verlassen wird der kurze Piepton jede Sekunde in den letzten 10 Sekunden schneller, sodass du spürst, wie die Zeit abläuft; beim Betreten erinnert dich ein 200-ms-Intervallton an die PIN-Eingabe.
Sirene und Relais anschließen
Im Sirenenkreis treibt der Arduino nur die Relaisspule; der 12-V-Sirenenstrom fließt über die Relaiskontakte. Die meisten Relaismodule sind Low-aktiv, ziehen also an, wenn der IN-Pin LOW ist; dafür gibt es im Code die Konstante ROLE_AKTIF_LOW. Der Code schreibt den Aus-Pegel, bevor er den Pin zum Ausgang macht; so zieht das Relais beim Booten nicht kurz an und die Sirene zirpt nicht. Achte bei der Sirene auf den Strom: Kleine 12-V-Sirenen mit 110 dB ziehen meist 0,1–0,3 A, eine winzige Last für einen 10-A-Relaiskontakt. Eine Außensirene mit Blitzleuchte schreckt mit Ton und Licht ab und zeigt den Nachbarn, woher der Alarm kommt. Professionelle Zentralen schalten die Sirene meist nach 3 Minuten ab, und manche Länder schreiben sogar eine Höchstdauer vor; halte SIREN_SURE daher vernünftig. Willst du statt eines Relais einen Halbleiterschalter, nimm einen Logic-Level-MOSFET und setze bei Sirenen mit Spule (elektromagnetisch) eine Diode in Sperrrichtung über die Anschlüsse.
Telegram-Benachrichtigung mit dem ESP32
Eine Sirene, die niemand hört, nützt nichts; der einfachste Weg, unterwegs von einem Alarm zu erfahren, sind ESP32 und Telegram. Der ESP32 verbindet sich mit dem WLAN und schickt Nachrichten über eine verschlüsselte Verbindung an den Telegram-Server, und du kannst ihm auch Befehle senden. Wie du mit BotFather einen Bot anlegst, Token und Chat-ID bekommst und die Bibliotheken UniversalTelegramBot und ArduinoJson installierst, erklären wir Schritt für Schritt im Artikel ESP32 Telegram-Bot. Diese Version hat kein Keypad; du steuerst das System vom Handy mit den Befehlen /kur (extern-scharf), /evde (intern-scharf), /coz (unscharf) und /durum (Status). Der Bot antwortet nur deiner Chat-ID und ignoriert Befehle von allen anderen stillschweigend.
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}Der ESP32-Code unterscheidet sich in drei wichtigen Punkten. Erstens kann das Senden einer Nachricht oder das Abfragen neuer Befehle 1–2 Sekunden dauern, und so lange wartet loop(). Eine offen stehende Tür und ein PIR, der mindestens einige Sekunden HIGH bleibt, sind davon nicht betroffen, die Millisekunden-Impulse des SW-420 könnten aber verloren gehen; deshalb wird die Erschütterung per Interrupt (attachInterrupt) erfasst. Zweitens schaltet alarmVer() zuerst die Sirene ein und schickt erst danach die Nachricht; egal wie langsam das Internet ist, die Sirene kommt nie zu spät. Nicht zugestellte Meldungen landen in einer Warteschlange und werden alle 15 Sekunden erneut gesendet. Drittens speichert die Bibliothek Preferences im Flash, ob die Anlage scharf ist: Fällt der Strom im Scharfzustand aus und kommt zurück, startet der ESP32 scharf und meldet dir, dass der Strom wieder da ist. Teilen sich Relais und Sirene ein schwaches Netzteil mit dem ESP32, kann das Board mit dem Fehler Brownout detector was triggered neu starten; die Lösung steht im Artikel ESP32 startet ständig neu.
Alarmanlage mit Raspberry Pi Pico und MicroPython
Denselben Zustandsautomaten baust du auf dem Raspberry Pi Pico mit einem kurzen MicroPython-Programm. Das Keypad wird mit der Zeilen-Spalten-Scanfunktion aus der Keypad-Anleitung gelesen; die Zeilen liegen an GP2–GP5, die Spalten mit internem Pull-up an GP6–GP9. Versorge den PIR aus VBUS (5 V); sein Ausgang hat ohnehin 3,3 V und kommt direkt an GP16. Den SW-420 versorgst du unbedingt aus dem Pin 3V3(OUT), denn der Ausgang eines mit 5 V betriebenen Moduls kann den Pico-Eingang beschädigen. Erschütterungsimpulse werden mit Pin.irq erfasst und ihre Zeiten in einer Liste gespeichert, die Zustände als einfache Zeichenketten geführt. Zeiten werden mit time.ticks_diff() berechnet, weil diese Funktion auch beim Überlauf des Zählers die richtige Differenz liefert.
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)So vermeidest du Fehlalarme
Der größte Feind einer Alarmanlage ist nicht der Einbrecher, sondern der Fehlalarm: Eine Sirene, die dreimal grundlos losging, nehmen weder du noch deine Nachbarn ernst. Einige der folgenden Maßnahmen stecken schon im Code, der Rest betrifft Montage und Verkabelung:
- Aufwärmzeit abwarten: Der PIR kann in den ersten 30–60 Sekunden nach dem Einschalten von selbst auslösen; der Code ignoriert die ersten 60 Sekunden.
- Filtern: Abzuwarten, bis ein Eingang eine bestimmte Zeit stabil ist (Entprellen), sortiert kurze Störimpulse auf den Leitungen aus.
- Schläge zählen: Statt eines einzelnen Klopfens 3 Schläge in 3 Sekunden zu verlangen, filtert Wind, vorbeifahrende Lkw und zuschlagende Türen heraus. Erhöhe die Zahl bei Bedarf auf 4–5 oder senke die Empfindlichkeit am Poti.
- PIR richtig platzieren: Nicht auf Heizkörper, Klimaanlage, sonnige Fenster oder Vorhänge richten; am besten erkennt er Bewegungen quer durch das Sichtfeld. Mit Haustieren den Sensor hoch montieren, leicht nach unten neigen und die Empfindlichkeit senken.
- Zwei Melder verlangen: In kritischen Bereichen zu fordern, dass PIR und Laser oder PIR und Tür innerhalb weniger Sekunden auslösen (Zweimelderabhängigkeit), verhindert, dass ein einzelner fehlerhafter Melder Alarm auslöst.
- Versorgung sauber halten: Der Spannungseinbruch beim Anziehen des Relais kann den PIR auslösen. Setze 100 µF und 100 nF zwischen VCC und GND jedes Sensors und führe Relais- und Sirenenleitungen getrennt von den Sensorleitungen.
- Externe Pull-ups an langen Leitungen: Der interne Pull-up des Arduino hat 20–50 kΩ und fängt auf 10 Metern Kabel Störungen ein; ein externer 4,7-kΩ-Widerstand plus 100 nF am Pin machen die Leitung deutlich robuster.
- Laser vor Umgebungslicht schützen: Setze den LDR in ein schwarzes Röhrchen und teste die Schwelle getrennt bei Tageslicht und nachts.
Häufige Fehler und Lösungen
- Alarm sofort nach dem Scharfschalten: Der PIR wärmt sich noch auf oder der Türkontakt ist falsch angeschlossen. Im Sensortest muss die geschlossene Tür LOW zeigen; steht dort HIGH, ist der Magnet zu weit weg oder eine Ader unterbrochen.
- Keypad reagiert nicht oder liefert falsche Tasten: Zeilen- und Spaltenadern sind vertauscht. Statt umzustecken, änderst du einfach die Reihenfolge in den Arrays satirPinleri und sutunPinleri.
- Langer Piepton beim Scharfschalten: Kein Defekt, sondern die Nicht-bereit-Warnung: Die Tür ist offen oder der Laserstrahl erreicht den LDR nicht. Mit der Taste D siehst du den Zustand der Zonen.
- Relais klickt beim Einschalten kurz: Der Aus-Pegel wurde nicht vor pinMode geschrieben, oder ROLE_AKTIF_LOW steht bei einem High-aktiven Relais noch auf true.
- Arduino startet neu, wenn die Sirene losgeht: Sirene und Arduino teilen sich ein schwaches Netzteil, und der Spannungseinbruch setzt das Board zurück. Nimm ein stärkeres Netzteil, einen separaten Wandler und dickere Kabel; Details unter Arduino startet ständig neu.
- Erschütterungssensor löst nie oder ständig aus: Dreh das Poti langsam, um die Schwelle einzustellen, und klebe den Sensor fest auf Glas oder Rahmen; ein locker sitzender Sensor überträgt den Schlag nicht.
- ESP32 sendet keine Nachrichten: Token oder Chat-ID sind falsch, das WLAN ist ein 5-GHz-Netz oder du hast den Bot in Telegram nie gestartet. Ein Bot kann dir erst schreiben, wenn du ihm einmal /start geschickt hast.
- Laser tagsüber in Ordnung, nachts Fehlalarm (oder umgekehrt): Die Schwelle liegt zu nah am Umgebungslicht. Miss die Werte mit und ohne Strahl in beiden Situationen und wähle LAZER_ESIK neu in der Mitte.
Ideen zum Erweitern der Alarmanlage
- Scharf und unscharf per Karte: Ergänze neben dem Keypad einen RFID-RC522-Leser und schalte mit einem Schlüsselanhänger unscharf.
- 24-Stunden-Zonen: Mit einem MQ-2-Gassensor oder einem Flammensensor baust du eine Gas- und Feuerzone, die auch im unscharfen Zustand Alarm gibt.
- Funk-Türkontakte: Baue für entfernte Fenster batteriebetriebene Kontakte mit 433-MHz-Funkmodulen.
- Bedienteil mit Display: Zeige Countdown, Status und Alarmspeicher auf einem I2C-LCD an.
- Smart-Home-Anbindung: Verbinde den ESP32 über MQTT mit Home Assistant und steuere die Alarmanlage zusammen mit Licht und Kameras.
- Sabotagekontakt: Ein Mikroschalter im Gehäusedeckel lässt den Summer ertönen, sobald jemand das Gehäuse öffnet, auch im unscharfen Zustand.
- Modellhaus-Projekt: Fasse alle Zonen in einem Modellhaus zusammen, als Sicherheitsebene eines Smart-Home-Projekts.
Weil sie Sensoren, Timing, Zustandsautomat und Stromversorgung in einem Projekt vereint, ist eine Arduino Alarmanlage auch zum Lernen ideal. Starte mit dem Sensortest, teste die Anlage dann ein paar Tage nur mit dem Summer und schließe die Sirene zuletzt an. In der BlueGrays-App findest du Projekte wie den Reedkontakt-Türsensor, das Laser-Sicherheitssystem, das 4x4-Keypad-LCD-Codeschloss und die ESP32-Bewegungsalarmanlage mit Telegram-Benachrichtigung, jeweils mit Schritt-für-Schritt-Schaltplänen und Code.
Häufige Fragen
Wie baut man eine Alarmanlage mit Arduino?
Du schließt an einen Arduino Uno einen PIR-Bewegungsmelder, einen Reedkontakt für die Tür, ein Keypad, einen aktiven Summer und ein Relaismodul für die Sirene an. Der Code ist ein Zustandsautomat, der zwischen unscharf, Ausgangsverzögerung, scharf, Eingangsverzögerung und Alarm wechselt. Scharf- und unscharf geschaltet wird per PIN am Keypad; löst im Scharfzustand ein Sensor aus, ertönt die Sirene.
Welche Sensoren braucht man für eine Arduino Alarmanlage?
Am häufigsten sind ein PIR-Bewegungsmelder (HC-SR501) für Innenräume, Reed- bzw. Magnetkontakte (MC-38) für Türen und Fenster, ein SW-420-Erschütterungssensor für Schläge auf Glas und eine Laser-Lichtschranke für Flure. Dazu kommen bei Bedarf ein MQ-2 für Gas und ein Flammensensor für Feuer. Jeder Sensor wird als eigene Zone mit eigenem Verhalten definiert.
Funktioniert eine Arduino Alarmanlage bei Stromausfall?
Nicht, wenn sie nur am Steckernetzteil hängt. Mit einem 13,8-V-Alarmnetzteil mit Akkuanschluss und einem 12-V-Akku mit 7 Ah läuft ein Uno-System mit etwa 100 mA rund 4–5 Tage. In der ESP32-Version wird der Scharfzustand im Flash gespeichert, sodass die Anlage nach der Rückkehr des Stroms scharf startet; damit Meldungen rausgehen, braucht auch der Router eine USV.
Kann eine Arduino Alarmanlage Benachrichtigungen aufs Handy schicken?
Ein Arduino Uno allein kann das nicht, ein ESP32 schickt aber per WLAN sofort Telegram-Nachrichten und nimmt Befehle wie /kur und /coz vom Handy entgegen. Wo kein Internet verfügbar ist, kann ein SIM800L-GSM-Modul SMS senden oder anrufen. Beide Wege zu kombinieren ist am zuverlässigsten.
Warum löst mein PIR-Bewegungsmelder Fehlalarme aus?
Typische Ursachen sind die Aufwärmzeit von 30–60 Sekunden nach dem Einschalten, Wärmequellen wie Heizkörper und Klimaanlagen, in der Sonne erwärmte, wehende Vorhänge und Störungen auf der Versorgung beim Schalten eines Relais. Die Aufwärmzeit im Code zu ignorieren, den Sensor von Wärmequellen fernzuhalten und einen Kondensator zwischen VCC und GND zu setzen, löst die meisten Probleme.
Kann man eine Arduino Alarmanlage im echten Zuhause nutzen?
Zum Lernen und zur Überwachung eines Zimmers, einer Garage oder eines Lagers funktioniert sie gut, eine zertifizierte Alarmanlage ersetzt sie aber nicht. Kommerzielle Systeme werden nach Normen wie EN 50131 auf Sabotageschutz, Notstromversorgung, verschlüsselte Funkverbindungen und Anbindung an eine Notrufleitstelle geprüft. Wenn du eine Arduino-Anlage zu Hause einsetzt, ergänze unbedingt Notstromakku, Sabotagekontakt und einen zweiten Meldeweg.
Projekte, die du danach bauen kannst
Alle Projekte ansehen
ESP32 Bewegungsalarmsystem (PIR + Buzzer + LED + Telegram)
Ein intelligentes Sicherheitsalarmsystem, das Buzzer und LED aktiviert, sobald der PIR-Sensor Bewegung erkennt, und gleichzeitig eine sofortige Benachrichtigung per Telegram an den Besitzer sendet. Ein 30-sekündiger Cooldown begrenzt die Häufigkeit wiederholter Alarme; dieser Mechanismus überprüft weder die Gültigkeit der PIR-Erkennungen noch verhindert er Fehlauslösungen. Geeignet für Heimsicherheit, Garagenüberwachung, Lagerräume oder Arbeitszimmer. Mit ESP32-CAM sind Foto-Benachrichtigungen m

Laser-Sicherheitssystem
Wie die Laser-Einbruchsszenen aus Filmen: Ein Laserstrahl trifft auf einen Lichtsensor, und sobald jemand dazwischentritt und den Strahl unterbricht, leuchtet die rote LED und der Summer legt los. Der

Laser-Warnsystem
Du baust einen unsichtbaren Lichtschrankenzaun wie im Film. Ein Laser leuchtet auf einen Lichtsensor auf der anderen Seite; sobald jemand hindurchgeht und den Strahl unterbricht, leuchtet die LED und

Türsensor mit Reedschalter
Erkenne an einem Licht, ob deine Tür offen oder zu ist. An der Tür befestigst du einen kleinen Magneten, am Rahmen einen Schalter, der Magnete spürt; ist die Tür zu, liegen beide nebeneinander und die

Passwortschloss mit 4x4-Tastenfeld und LCD
Ein kleines elektronisches Türschloss, das niemanden ohne den richtigen Code hereinlässt. Du drückst die Rautetaste und tippst den Code auf dem Tastenfeld ein; stimmt er, meldet das Display Zutritt er

Bewegungsmelder mit Bewegungssensor
Ein einfacher Wächter, der Bescheid gibt, wenn jemand den Raum betritt. Der Bewegungsmelder spürt die Wärme, die der menschliche Körper abgibt; der Arduino lässt die LED leuchten, solange sich etwas b

Intelligenter elektronischer Safe
Ein kleiner Tresor für deine Wertsachen, der sich mit einem Code öffnet. Du tippst deinen vierstelligen Code auf dem Tastenfeld ein; stimmt er, öffnet der Servomotor das Schloss, das grüne Licht geht
Weitere Artikel
BlueGrays-App
Diese Schaltung Schritt für Schritt auf dem Handy nachbauen
Schaltplan, Code und Teileliste auf einem Bildschirm. Über 200 Arduino-, ESP32- und Pico-Projekte zum Nachbauen.
Mit dem Handy scannen
Richte die Kamera auf den Code, dann öffnet sich der App-Store deines Handys.
oder im Store öffnen

