Alarme Arduino maison : PIR, capteur de porte, clavier et sirène
Fabrique ton alarme Arduino : PIR, contact de porte ILS, capteurs de vibration et laser, code au clavier, temporisation d'entrée, sirène et alertes Telegram.
Une alarme Arduino est un système de sécurité que tu peux fabriquer toi-même : elle déclenche une sirène quand quelqu'un entre sans autorisation chez toi ou dans ta chambre, te laisse quelques secondes pour taper ton code quand la porte s'ouvre et, si tu le veux, t'envoie une notification instantanée sur ton téléphone. Dans ce guide, tu vas construire une alarme à quatre zones avec un détecteur de mouvement PIR, un contact reed (ILS) sur la porte, un capteur de vibration qui repère les chocs sur la vitre et une barrière laser qui traverse le couloir. Tu l'armeras et la désarmeras avec un code sur un clavier 4x4, tu commanderas une sirène 12 V par un relais et tu géreras le tout avec un seul Arduino Uno.
Étape par étape, nous allons reproduire en petit ce que font les centrales d'alarme du commerce : temporisation d'entrée et de sortie, mode partiel (mode nuit), blocage après des codes erronés, durée de sirène limitée et mémoire d'alarme. Au lieu de blocs if imbriqués, nous écrirons le code sous forme de machine à états ; ainsi, on saura toujours clairement ce que fait l'alarme à chaque instant. Ensuite, tu porteras la même logique sur un ESP32 pour armer, désarmer et recevoir les alertes via Telegram, tu écriras une version MicroPython pour le Raspberry Pi Pico et tu verras comment réduire le problème le plus agaçant de tous : les fausses alarmes.

Comment fonctionne une alarme Arduino ?
Tout système d'alarme comporte trois parties : des entrées (les capteurs), un cerveau qui décide (l'Arduino) et des sorties (sirène, buzzer, notification). Chaque capteur surveille une zone. Les centrales professionnelles classent les zones selon leur comportement lorsqu'elles se déclenchent, et c'est cette distinction qui sépare une bonne alarme d'un gadget agaçant qui hurle à chaque ouverture de porte. Notre système aura quatre zones :
| Zone | Capteur | Type de zone | Si déclenchée en mode armé |
|---|---|---|---|
| Porte d'entrée | Contact reed (MC-38) | Temporisée | Lance une temporisation d'entrée de 20 s ; alarme si aucun code n'est saisi |
| Salon, intérieur | PIR (HC-SR501) | Intérieure (suiveuse) | Alarme immédiate ; attend si la temporisation d'entrée tourne ; coupée en mode partiel |
| Fenêtre | Vibration (SW-420) | Immédiate | Alarme immédiate après 3 chocs en 3 s |
| Couloir, escalier | Laser + LDR (KY-008) | Intérieure (suiveuse) | Alarme immédiate ; attend si la temporisation d'entrée tourne |
| Gaz, feu, autoprotection | MQ-2, capteur de flamme, contact de boîtier | 24 h/24 | Alarme même désarmée (idée d'amélioration) |
Grâce à la zone temporisée, la sirène ne hurle pas dès que tu rentres : la porte s'ouvre, le buzzer bipe pour te prévenir et tu as 20 secondes pour taper ton code. Une zone intérieure suiveuse reste patiente pendant la temporisation d'entrée ; passer devant le PIR en allant de la porte au clavier ne déclenche donc rien. Mais si quelqu'un entre sans ouvrir la porte, par exemple par une fenêtre, et passe devant le PIR, aucune temporisation n'a démarré et l'alarme se déclenche aussitôt. Une zone immédiate se déclenche toujours sur-le-champ ; inutile d'offrir 20 secondes à quelqu'un qui force une fenêtre. En sortant, c'est le même principe : quand tu armes le système, une temporisation de sortie de 30 secondes démarre et passer la porte pendant ce temps ne déclenche rien.
Liste du matériel pour l'alarme Arduino
| Matériel | Qté | Rôle |
|---|---|---|
| Arduino Uno (ou Nano) | 1 | Le cerveau du système ; les broches de ce guide sont les mêmes sur Uno et Nano |
| Détecteur de mouvement PIR HC-SR501 | 1 | Détection de mouvement intérieure, 3–7 m, environ 110° |
| Contact d'ouverture MC-38 (ou ILS + aimant) | 1 | Ouvre le circuit quand une porte ou une fenêtre s'ouvre |
| Module capteur de vibration SW-420 | 1 | Détecte les chocs sur la vitre ou le cadre |
| Module laser KY-008 + LDR + résistance de 10 kΩ | 1 lot | Barrière lumineuse à travers le couloir |
| Clavier à membrane 4x4 | 1 | Armement et désarmement par code |
| Buzzer actif (5 V) | 1 | Bip des touches, alerte d'entrée et de sortie, sirène intérieure |
| Module relais 1 voie 5 V | 1 | Commande la sirène 12 V isolée de l'Arduino |
| Sirène 12 V (idéalement avec flash) | 1 | Le son d'alarme principal, en général 100–120 dB |
| LED rouge et verte + 2 résistances de 220 Ω | 1 lot | Voyants armé et prêt |
| Alimentation 12 V, module LM2596, batterie 12 V (en option) | 1 lot | Alimentation et secours en cas de coupure |
| Breadboard, fils Dupont, câble d'alarme 4 conducteurs | - | Connexions et capteurs éloignés |







Les capteurs : que détecte chaque zone ?
- PIR (HC-SR501) : Il détecte les variations du rayonnement infrarouge émis par le corps humain et voit les mouvements à 3–7 mètres sur un angle d'environ 110 degrés. Sa sortie est en 3,3 V, mais il doit être alimenté en 5 V. Pendant les 30–60 secondes qui suivent la mise sous tension, il apprend la pièce et peut se déclencher tout seul ; c'est pourquoi le code ignore la première minute. Plus de détails dans l'article détecteur de mouvement PIR.
- Contact de porte magnétique (MC-38) : Deux boîtiers en plastique, l'un vissé sur le dormant, l'autre sur l'ouvrant. Porte fermée, l'aimant est proche et le contact est fermé ; dès que la porte s'ouvre de plus de 15–25 mm, le contact s'ouvre. Il se lit avec une seule broche et la résistance de tirage interne ; nous expliquons ce qu'il contient dans l'article interrupteur reed (ILS).
- Capteur de vibration (SW-420) : Un interrupteur normalement fermé avec une tige métallique entourée d'un ressort. Quand la vitre ou le cadre reçoit un choc, le contact s'ouvre un instant et le comparateur LM393 de la carte en fait une impulsion numérique. Sur la plupart des modules, la sortie est à LOW au repos et passe brièvement à HIGH lors d'un choc ; vérifie ton module avec le test des capteurs. La sensibilité se règle avec le potentiomètre ; détails dans l'article capteur de vibration SW-420.
- Barrière laser (KY-008 + LDR) : Un laser de 650 nm d'environ 5 mW éclaire en permanence une LDR de l'autre côté du couloir. Quand le faisceau est coupé, la résistance de la LDR passe de quelques centaines d'ohms à plusieurs dizaines de kiloohms et la lecture analogique chute nettement. Conseils de montage et d'alignement dans l'article module laser.
- Clavier 4x4 : Une matrice de 16 touches formée de 4 lignes et 4 colonnes ; il utilise 8 broches et se lit en une ligne avec la bibliothèque Keypad. Le principe du balayage lignes-colonnes est expliqué dans le guide du clavier matriciel.
- Sirène et buzzer : Un buzzer actif possède son propre oscillateur et bipe dès que la broche passe à HIGH ; il est idéal pour le bip des touches et les alertes d'entrée et de sortie. Le son vraiment dissuasif vient de la sirène 12 V, que tu commandes via un module relais. La différence entre buzzer actif et passif est expliquée dans qu'est-ce qu'un buzzer.
Schéma de câblage et tableau des broches
Le tableau ci-dessous réunit le câblage des trois cartes. La version Arduino Uno utilise les quatre zones et le clavier. Comme la version ESP32 s'arme et se désarme par commandes Telegram au lieu d'un clavier et que la version Pico reste courte, la barrière laser n'existe que sur l'Uno. Pour voir les broches de ta carte, utilise l'outil Pinout, et pour calculer les résistances des LED, le calculateur de résistance pour LED.
| Connexion | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| Clavier R1–R4 (lignes) | D9, D8, D7, D6 | – (Telegram) | GP2, GP3, GP4, GP5 |
| Clavier C1–C4 (colonnes) | D5, D4, D3, D2 | – (Telegram) | GP6, GP7, GP8, GP9 |
| PIR VCC / OUT / GND | 5V / D10 / GND | VIN (5V) / GPIO 27 / GND | VBUS / GP16 / GND |
| Contact reed (2 fils) | D11 et GND | GPIO 33 et GND | GP15 et GND |
| SW-420 VCC / DO / GND | 5V / D12 / GND | 3V3 / GPIO 32 / GND | 3V3(OUT) / GP17 / GND |
| KY-008 S / − (laser toujours allumé) | 5V / GND | – | – |
| LDR entre 5V et A0, 10 kΩ entre A0 et GND | A0 | – | – |
| Buzzer actif + (− à GND) | A1 | GPIO 25 | GP14 |
| IN du module relais | A2 | GPIO 26 | GP13 |
| LED verte (avec 220 Ω) : prêt | A3 | – | – |
| LED rouge (avec 220 Ω) : armé | D13 | GPIO 2 (LED intégrée) | LED intégrée |
Une alarme est surtout utile quand le courant est coupé, alors pense à l'alimentation dès le départ. Un montage pratique : comme sur les centrales professionnelles, une alimentation d'alarme 13,8 V avec raccordement de batterie alimente la sirène et, abaissée à 5 V par un convertisseur abaisseur LM2596, la broche 5V de l'Arduino. En cas de coupure, une batterie au plomb 12 V 7 Ah prend le relais. Le calcul est simple : l'Uno, le PIR, le laser, les LED et le SW-420 consomment environ 100 mA sous 5 V au repos ; cela fait 0,5 W, soit environ 50 mA côté batterie avec un convertisseur à 85 % de rendement. Même en n'utilisant que 80 % de sa capacité, une batterie de 7 Ah fait tenir le système 4–5 jours ; les 0,2–0,5 A de la sirène quand elle sonne réduisent un peu cette durée. Essaie tes propres valeurs avec le calculateur d'autonomie de batterie et consulte comment alimenter un Arduino pour les options d'alimentation.
Montage étape par étape
- Dans l'Arduino IDE, ouvre le gestionnaire de bibliothèques, tape Keypad et installe la bibliothèque de Mark Stanley et Alexander Brevig. Sans elle, tu obtiendras l'erreur Keypad.h: No such file or directory à la compilation.
- Tiens le clavier touches face à toi ; branche les quatre premiers fils en partant de la gauche (R1–R4) sur D9, D8, D7, D6 et les quatre suivants (C1–C4) sur D5, D4, D3, D2.
- Branche le PIR sur 5V, D10 et GND ; tourne les deux potentiomètres à fond dans le sens antihoraire et place le cavalier de déclenchement sur H.
- Branche les deux fils du contact reed sur D11 et GND, et le SW-420 sur 5V, D12 et GND.
- Place la LDR entre 5V et A0 et la résistance de 10 kΩ entre A0 et GND ; mets la LDR au fond d'un tube noir et vise le centre du tube avec le laser.
- Branche le buzzer sur A1, l'entrée IN du module relais sur A2 et ses VCC et GND sur le 5V et le GND de l'Arduino ; câble les LED sur A3 et D13 avec des résistances de 220 Ω.
- Téléverse d'abord le test des capteurs ci-dessous, ouvre le moniteur série à 9600 bauds et déclenche chaque capteur un par un.
- Téléverse ensuite le code complet de l'alarme, change le code et teste le système quelques jours avec le buzzer seul avant de brancher la sirène.
Premier test : voir tous les capteurs dans le moniteur série
Avant de passer au code de l'alarme, assure-toi que chaque capteur se comporte comme prévu. Ce petit programme affiche deux fois par seconde l'état du PIR, si la porte est ouverte ou fermée, le nombre d'impulsions de vibration de la dernière demi-seconde et la valeur analogique de la LDR. La broche de vibration est lue en permanence, car les impulsions du SW-420 peuvent ne durer que quelques millisecondes et un code avec delay() entre deux lectures les manquerait.
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}Machine à états : le cerveau de l'alarme
Si tu essaies d'écrire une alarme avec des blocs if et delay(), cela devient vite un sac de nœuds : pendant l'attente de la temporisation d'entrée, personne ne lit le clavier, et pendant que la sirène hurle, impossible de taper le code. La solution est une machine à états, où le système se trouve à chaque instant dans un seul état et n'en change que sur des événements précis. Dans le code, les états sont définis par un enum, loop() n'applique à chaque passage que les règles de l'état courant et les attentes se mesurent avec millis() au lieu de delay(). Ainsi, la boucle tourne des milliers de fois par seconde et aucun appui de touche ni événement de capteur n'est perdu.
| État | Quand y entre-t-on ? | Que fait-il ? | Quand en sort-on ? |
|---|---|---|---|
| DÉSARMÉ | À la mise sous tension et après code + # | Surveille les capteurs sans jamais déclencher ; la LED verte s'allume si tout est prêt | Code + A ou code + B |
| TEMPO DE SORTIE | Code + A (total) ou B (partiel) | Décompte 30 s ; le buzzer bipe chaque seconde, plus vite les 10 dernières | ARMÉ à la fin ; DÉSARMÉ avec code + # |
| ARMÉ | À la fin de la temporisation de sortie | Surveille toutes les zones ; la LED rouge reste allumée | ENTRÉE si la porte s'ouvre ; ALARME sur zone immédiate ou intérieure |
| TEMPO D'ENTRÉE | Porte ouverte en mode armé | Le buzzer bipe pendant 20 s ; PIR et laser patientent | DÉSARMÉ avec code + # ; ALARME à la fin du délai |
| ALARME | Une zone se déclenche | Sirène et buzzer sonnent ; la sirène s'arrête après 3 min, la LED rouge clignote vite | DÉSARMÉ uniquement avec code + # |
Armer, désarmer et le code au clavier
| Touches | Fonction |
|---|---|
| 2580 + A | Mode total : les quatre zones actives, 30 s de temporisation de sortie |
| 2580 + B | Mode partiel (nuit) : PIR coupé ; porte, fenêtre et laser actifs |
| 2580 + # | Désarme ; en cas d'alarme, affiche la zone déclenchée dans le moniteur série |
| * ou C | Efface un code mal tapé |
| D | Test des zones : affiche les valeurs de la porte, du PIR et du laser dans le moniteur série |
| 3 codes faux | Le clavier se bloque 60 s ; même le bon code est refusé pendant ce temps |
Demander le code aussi pour armer évite que des enfants ou des invités activent le système par erreur. Le code fait en plus une vérification non prêt : si tu essaies d'armer avec la porte ouverte ou le faisceau laser coupé, tu entends un long bip et le système ne s'arme pas, exactement comme sur les centrales du commerce. Ne garde pas le code par défaut 2580 et évite les codes faciles comme 1234, 0000 ou ton année de naissance. Pour changer le code sans retéléverser le programme, enregistre-le dans l'EEPROM et ajoute un menu qui permet de saisir un nouveau code au clavier. Avec un écran LCD I2C à côté du clavier, tu verras aussi le compte à rebours et les messages d'état ; c'est pour cela que les broches A4 et A5 sont restées libres.
Code de l'alarme Arduino
Le code ci-dessous est le cœur de ce guide. Dans la section des réglages en haut, tu peux modifier le code, les temporisations, la durée de la sirène, le seuil du laser et le type de relais ; inutile de toucher au reste. Sur un Arduino Uno, il occupe environ 6 Ko de mémoire programme et 620 octets de RAM, il reste donc largement de quoi ajouter un écran LCD ou un lecteur RFID.
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}La colonne vertébrale du code est la fonction suz(). Chaque fois que la valeur brute d'une entrée change, un horodatage est pris, et l'état stable ne change que si la valeur reste identique pendant une durée fixe (50 ms pour la porte, 200 ms pour le PIR, 30 ms pour le laser). Ce filtre simple empêche les parasites brefs sur les longs câbles et les rebonds de contact de déclencher l'alarme. Lire la broche de la porte avec INPUT_PULLUP est aussi un choix délibéré : porte fermée, le reed relie la broche à GND et on lit LOW ; quand la porte s'ouvre ou que quelqu'un coupe le câble, la broche passe à HIGH. Cette logique en boucle fermée des vraies alarmes traite un fil coupé exactement comme une porte ouverte.
La vibration est traitée autrement. Même un seul choc fait rebondir le SW-420 plusieurs fois en quelques millisecondes ; titresimVar() compte d'abord comme un seul événement tout ce qui arrive en 150 ms, puis garde l'heure des trois derniers événements. Si trois événements tiennent en 3 secondes, c'est considéré comme une vraie tentative d'effraction ; le vent qui secoue la porte ou un ballon contre la vitre ne déclenchent rien. La sirène se tait d'elle-même au bout de 3 minutes, mais le système reste dans l'état ALARME et la LED rouge clignote vite pour prévenir la personne qui rentre qu'il y a eu une alarme. Quand tu tapes le bon code, le nom de la zone enregistré dans la variable tetikleyen s'affiche dans le moniteur série : c'est une petite version de la mémoire d'alarme des centrales du commerce.
Comment régler les temporisations d'entrée et de sortie
Des temporisations trop courtes et tu déclencheras ta propre alarme ; trop longues et tu offres du temps au cambrioleur. Choisis une temporisation de sortie égale au double du temps qu'il te faut pour aller du clavier à la porte et la fermer à clé, et une temporisation d'entrée d'environ le double du temps pour ouvrir la porte, aller au clavier et taper ton code. Les centrales du commerce utilisent souvent 30–60 secondes pour la sortie et 15–30 secondes pour l'entrée. Placer le clavier juste à côté de la porte, mais invisible de l'extérieur, permet de garder une temporisation d'entrée courte. Le comportement du buzzer compte aussi : en sortie, un bip court chaque seconde accélère pendant les 10 dernières secondes pour que tu sentes le temps filer ; en entrée, un bip intermittent de 200 ms te rappelle de taper ton code.
Brancher la sirène et le relais
Dans le circuit de la sirène, l'Arduino ne pilote que la bobine du relais ; le courant 12 V de la sirène passe par les contacts du relais. La plupart des modules relais sont actifs à l'état bas : ils collent quand la broche IN est à LOW, d'où la constante ROLE_AKTIF_LOW dans le code. Le code écrit le niveau « éteint » avant de configurer la broche en sortie ; ainsi, le relais ne colle pas un instant au démarrage de l'Arduino et la sirène ne couine pas. Pour choisir la sirène, regarde sa consommation : les petites sirènes 12 V de 110 dB consomment en général 0,1–0,3 A, une charge minime pour un contact de relais de 10 A. Une sirène extérieure avec flash dissuade par le son et la lumière et montre aux voisins d'où vient l'alarme. Les centrales professionnelles coupent généralement la sirène au bout de 3 minutes, et certains pays imposent même une durée maximale ; garde donc SIREN_SURE raisonnable. Si tu préfères un interrupteur statique à un relais, tu peux utiliser un MOSFET à niveau logique et, pour les sirènes à bobine (électromagnétiques), ajouter une diode en inverse entre leurs bornes.
Alertes Telegram avec l'ESP32
Une sirène que personne n'entend ne sert à rien ; le moyen le plus simple de savoir que ton alarme s'est déclenchée pendant ton absence, c'est un ESP32 et Telegram. L'ESP32 se connecte au Wi-Fi et envoie des messages au serveur de Telegram par une connexion chiffrée, et tu peux aussi lui envoyer des commandes. Nous expliquons pas à pas comment créer un bot avec BotFather, obtenir le token et ton chat ID et installer les bibliothèques UniversalTelegramBot et ArduinoJson dans l'article bot Telegram avec l'ESP32. Cette version n'a pas de clavier ; on la pilote depuis le téléphone avec les commandes /kur (armement total), /evde (mode partiel), /coz (désarmer) et /durum (état). Le bot ne répond qu'à ton chat ID et ignore en silence les commandes de toute autre personne.
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}Le code ESP32 présente trois différences importantes. D'abord, envoyer un message à Telegram ou demander les nouvelles commandes peut prendre 1–2 secondes, et loop() attend pendant ce temps. Une porte restée ouverte et un PIR qui reste à HIGH au moins quelques secondes n'en souffrent pas, mais les impulsions de quelques millisecondes du SW-420 pourraient être perdues ; c'est pourquoi la vibration est captée par une interruption (attachInterrupt). Ensuite, alarmVer() allume d'abord la sirène puis envoie le message ; aussi lente que soit la connexion, la sirène n'est jamais retardée. Les notifications qui échouent sont mises en file d'attente et renvoyées toutes les 15 secondes. Enfin, la bibliothèque Preferences enregistre dans la mémoire flash si le système est armé : si le courant est coupé puis revient alors qu'il était armé, l'ESP32 redémarre armé et te prévient du retour du courant. Si le relais et la sirène partagent une alimentation faible avec l'ESP32, la carte peut redémarrer avec l'erreur Brownout detector was triggered ; la solution est dans l'article l'ESP32 redémarre en boucle.
Alarme avec le Raspberry Pi Pico et MicroPython
Tu peux construire la même machine à états sur un Raspberry Pi Pico avec un court programme MicroPython. Le clavier est lu avec la fonction de balayage lignes-colonnes du guide du clavier matriciel ; les lignes sont sur GP2–GP5 et les colonnes, avec tirage interne, sur GP6–GP9. Alimente le PIR par VBUS (5 V) ; sa sortie étant déjà en 3,3 V, elle se branche directement sur GP16. Le SW-420, lui, doit impérativement être alimenté par la broche 3V3(OUT), car la sortie d'un module alimenté en 5 V peut endommager l'entrée du Pico. Les impulsions de vibration sont captées avec Pin.irq et leurs instants gardés dans une liste, tandis que les états sont suivis sous forme de simples chaînes. Les durées se calculent avec time.ticks_diff(), car cette fonction donne le bon écart même quand le compteur repart à zéro.
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)Comment réduire les fausses alarmes
Le pire ennemi d'un système d'alarme n'est pas le cambrioleur, mais la fausse alarme : une sirène qui a sonné trois fois pour rien, plus personne ne la prend au sérieux, ni toi ni tes voisins. Une partie des mesures suivantes est déjà dans le code ; le reste concerne le montage et le câblage :
- Attends le temps de chauffe : Le PIR peut se déclencher seul pendant les 30–60 premières secondes après la mise sous tension ; le code ignore la première minute.
- Utilise un filtre : Attendre que chaque entrée reste stable un certain temps (anti-rebond) élimine les parasites brefs sur les câbles.
- Compte les chocs : Exiger 3 chocs en 3 secondes au lieu d'un seul coup écarte le vent, les camions qui passent et les portes qui claquent. Si besoin, monte à 4–5 chocs ou baisse la sensibilité avec le potentiomètre.
- Place bien le PIR : Ne le pointe pas vers un radiateur, une clim, une fenêtre ensoleillée ou des rideaux ; il détecte le mieux quand le mouvement traverse son champ de vision de côté. Si tu as des animaux, monte-le en hauteur, incline-le un peu vers le bas et baisse la sensibilité.
- Exige deux capteurs : Dans les zones critiques, demander que le PIR et le laser, ou le PIR et la porte, se déclenchent à quelques secondes d'intervalle (zones croisées) empêche un seul capteur défaillant de provoquer une alarme.
- Soigne l'alimentation : La chute de tension quand le relais colle peut déclencher le PIR. Mets des condensateurs de 100 µF et 100 nF entre VCC et GND de chaque capteur et fais passer les fils du relais et de la sirène à l'écart de ceux des capteurs.
- Résistances de tirage externes sur les longs câbles : La résistance interne de l'Arduino fait 20–50 kΩ et capte les parasites sur 10 mètres de câble ; une résistance externe de 4,7 kΩ et un condensateur de 100 nF sur la broche rendent la ligne bien plus robuste.
- Protège le laser de la lumière ambiante : Place la LDR dans un tube noir et teste le seuil séparément en plein jour et la nuit.
Erreurs fréquentes et solutions
- L'alarme se déclenche dès qu'elle est armée : Le PIR chauffe encore ou le contact de porte est mal branché. Dans le test des capteurs, la porte fermée doit lire LOW ; si elle lit HIGH, l'aimant est trop loin ou un fil est coupé.
- Le clavier ne répond pas ou lit la mauvaise touche : Les fils des lignes et des colonnes sont inversés. Au lieu de recâbler, change l'ordre des tableaux satirPinleri et sutunPinleri dans le code.
- Un long bip retentit quand tu essaies d'armer : Ce n'est pas une panne mais l'alerte non prêt : la porte est ouverte ou le faisceau laser n'atteint pas la LDR. Appuie sur D pour voir l'état des zones.
- Le relais claque un instant à la mise sous tension : Le niveau « éteint » n'a pas été écrit avant de passer la broche en sortie, ou ROLE_AKTIF_LOW est resté à true avec un relais actif à l'état haut.
- L'Arduino redémarre quand la sirène sonne : La sirène et l'Arduino partagent un adaptateur trop faible et la chute de tension fait redémarrer la carte. Utilise un adaptateur plus puissant, un convertisseur séparé et des fils plus gros ; détails dans l'Arduino redémarre tout seul.
- Le capteur de vibration ne se déclenche jamais ou tout le temps : Tourne lentement le potentiomètre pour régler le seuil et colle fermement le capteur sur la vitre ou le cadre ; un capteur mal fixé ne transmet pas le choc.
- L'ESP32 n'envoie pas de messages : Le token ou le chat ID est faux, le réseau est en 5 GHz ou tu n'as jamais démarré le bot dans Telegram. Un bot ne peut pas t'écrire tant que tu ne lui as pas envoyé /start une fois.
- Le laser marche le jour mais déclenche la nuit (ou l'inverse) : Le seuil est trop proche du niveau de lumière ambiante. Mesure les valeurs avec et sans faisceau dans les deux situations et choisis un nouveau LAZER_ESIK au milieu.
Idées pour améliorer ton système d'alarme
- Armer et désarmer avec un badge : Ajoute un lecteur RFID RC522 à côté du clavier et désarme avec un porte-clés.
- Zones 24 h/24 : Avec un capteur de gaz MQ-2 ou un capteur de flamme, crée une zone gaz et incendie qui déclenche même quand le système est désarmé.
- Contacts de porte sans fil : Fabrique des contacts sur pile pour les fenêtres éloignées avec des modules RF 433 MHz.
- Clavier avec écran : Affiche le compte à rebours, l'état et la mémoire d'alarme sur un LCD I2C.
- Intégration domotique : Relie l'ESP32 à MQTT et Home Assistant pour gérer l'alarme avec tes lumières et tes caméras.
- Contact d'autoprotection : Place un micro-interrupteur sous le couvercle du boîtier pour que le buzzer sonne si on l'ouvre, même désarmé.
- Projet de maquette : Réunis toutes ces zones dans une maison miniature comme couche de sécurité d'un projet de maison connectée.
Parce qu'elle réunit lecture de capteurs, gestion du temps, machine à états et conception de l'alimentation dans un seul projet, une alarme Arduino est aussi une excellente façon d'apprendre. Lance d'abord le test des capteurs, essaie ensuite l'alarme quelques jours avec le buzzer seul et branche la sirène en dernier. Dans l'application BlueGrays, tu trouveras des projets comme le capteur de porte à ILS, le système de sécurité laser, la serrure à code avec clavier 4x4 et LCD et le système d'alarme de mouvement ESP32 avec alertes Telegram, chacun avec schémas et code pas à pas.
Questions fréquentes
Comment faire une alarme avec Arduino ?
Tu branches sur un Arduino Uno un détecteur de mouvement PIR, un contact reed pour la porte, un clavier, un buzzer actif et un module relais qui commande une sirène. Le code est une machine à états qui fait passer le système par les états désarmé, temporisation de sortie, armé, temporisation d'entrée et alarme. On l'arme et le désarme avec un code au clavier ; si un capteur se déclenche en mode armé, la sirène sonne.
Quels capteurs utiliser pour une alarme Arduino ?
Les plus courants sont un détecteur de mouvement PIR (HC-SR501) pour l'intérieur, des contacts d'ouverture reed (MC-38) pour les portes et fenêtres, un capteur de vibration SW-420 pour les chocs sur les vitres et une barrière laser pour les couloirs. On peut ajouter un MQ-2 pour le gaz et un capteur de flamme pour l'incendie. Chaque capteur est défini comme une zone distincte avec son propre comportement.
Une alarme Arduino fonctionne-t-elle pendant une coupure de courant ?
Pas si elle n'est alimentée que par un adaptateur secteur. Avec une alimentation d'alarme 13,8 V avec batterie et une batterie 12 V 7 Ah, un système Uno qui consomme environ 100 mA tient 4–5 jours. Dans la version ESP32, l'état armé est enregistré en flash, donc le système redémarre armé au retour du courant ; pour que les alertes partent, la box doit aussi être sur onduleur.
Une alarme Arduino peut-elle envoyer des notifications sur mon téléphone ?
Un Arduino Uno seul ne le peut pas, mais un ESP32 envoie des messages Telegram instantanés en Wi-Fi et reçoit depuis le téléphone des commandes comme /kur et /coz. Là où il n'y a pas d'internet, un module GSM SIM800L peut envoyer un SMS ou passer un appel. Combiner les deux méthodes est la solution la plus fiable.
Pourquoi mon détecteur PIR déclenche-t-il de fausses alarmes ?
Les causes les plus fréquentes sont le temps de chauffe de 30–60 secondes après la mise sous tension, les sources de chaleur comme les radiateurs et la clim, les rideaux qui bougent chauffés par le soleil et les parasites d'alimentation quand un relais commute. Ignorer le temps de chauffe dans le code, éloigner le capteur des sources de chaleur et placer un condensateur entre VCC et GND règlent la plupart des cas.
Peut-on utiliser une alarme Arduino dans une vraie maison ?
Elle fonctionne bien pour apprendre et pour surveiller une chambre, un garage ou une cave, mais elle ne remplace pas un système d'alarme certifié. Les systèmes du commerce sont testés selon des normes comme l'EN 50131 pour l'autoprotection, l'alimentation de secours, les liaisons radio chiffrées et le raccordement à un centre de télésurveillance. Si tu utilises une alarme Arduino chez toi, ajoute au minimum une batterie de secours, un contact d'autoprotection et un second canal d'alerte.
Projets à réaliser après cet article
Voir tous les projets
Système d'Alarme de Mouvement ESP32 (PIR + Buzzer + LED + Telegram)
Un système d'alarme de sécurité intelligent qui active le buzzer et la LED lorsque le capteur PIR détecte un mouvement, tout en envoyant une notification instantanée au propriétaire via Telegram. Il intègre un délai d'attente de 30 secondes pour limiter la fréquence des alarmes répétées ; ce mécanisme ne vérifie pas la validité des détections du capteur PIR et n'élimine pas les faux déclenchements. Idéal pour la sécurité domestique, la surveillance de garage, d'entrepôts ou de bureaux. Avec l'ES

Système de sécurité laser
Comme dans les scènes de cambriolage au laser des films : un rayon laser éclaire un capteur de lumière, et dès que quelqu'un passe et coupe le rayon, la LED rouge s'allume et le buzzer se met à sonner

Système d'alerte laser
Tu installes une barrière de faisceau invisible, comme dans les films. Un laser éclaire un capteur de lumière de l'autre côté ; dès que quelqu'un passe et coupe le faisceau, la LED s'allume et le buzz

Capteur de porte avec interrupteur reed
Sache si ta porte est ouverte ou fermée rien qu'en regardant une lumière. Tu fixes un petit aimant sur la porte et sur le cadre un interrupteur qui sent les aimants ; porte fermée, ils sont côte à côt

Serrure à code avec clavier 4x4 et écran LCD
Une petite serrure électronique qui ne laisse entrer que ceux qui connaissent le code. Tu appuies sur la touche dièse et tapes le code sur le clavier ; s'il est bon, l'écran affiche accès autorisé, la

Détecteur de mouvement avec capteur de mouvement
Un gardien tout simple qui te prévient quand quelqu'un entre dans la pièce. Le détecteur de mouvement repère la chaleur dégagée par le corps humain ; l'Arduino garde la LED allumée tant qu'il y a du m

Coffre-fort électronique intelligent
Un petit coffre-fort pour tes objets précieux, qui s'ouvre avec un code. Tu tapes ton code à quatre chiffres sur le clavier ; s'il est bon, le servomoteur ouvre le verrou, la lumière verte s'allume et
Autres articles
Application BlueGrays
Réalise ce montage pas à pas sur ton téléphone
Schéma, code et liste des composants sur un seul écran. Plus de 200 projets Arduino, ESP32 et Pico à réaliser.
Scanne avec ton téléphone
Pointe l’appareil photo vers le code : la boutique d’applications de ton téléphone s’ouvre.
ou ouvre la boutique

