Guides · 21 min de lecture

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.

Détecteur de mouvement PIR HC-SR501
Le détecteur de mouvement PIR HC-SR501 forme la zone intérieure de l'alarme : sa lentille de Fresnel blanche découpe le champ de vision en dizaines de petites zones, et les deux potentiomètres voisins règlent la portée et la durée de sortie.

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 :

ZoneCapteurType de zoneSi déclenchée en mode armé
Porte d'entréeContact reed (MC-38)TemporiséeLance une temporisation d'entrée de 20 s ; alarme si aucun code n'est saisi
Salon, intérieurPIR (HC-SR501)Intérieure (suiveuse)Alarme immédiate ; attend si la temporisation d'entrée tourne ; coupée en mode partiel
FenêtreVibration (SW-420)ImmédiateAlarme immédiate après 3 chocs en 3 s
Couloir, escalierLaser + LDR (KY-008)Intérieure (suiveuse)Alarme immédiate ; attend si la temporisation d'entrée tourne
Gaz, feu, autoprotectionMQ-2, capteur de flamme, contact de boîtier24 h/24Alarme 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érielQtéRôle
Arduino Uno (ou Nano)1Le cerveau du système ; les broches de ce guide sont les mêmes sur Uno et Nano
Détecteur de mouvement PIR HC-SR5011Détection de mouvement intérieure, 3–7 m, environ 110°
Contact d'ouverture MC-38 (ou ILS + aimant)1Ouvre le circuit quand une porte ou une fenêtre s'ouvre
Module capteur de vibration SW-4201Détecte les chocs sur la vitre ou le cadre
Module laser KY-008 + LDR + résistance de 10 kΩ1 lotBarrière lumineuse à travers le couloir
Clavier à membrane 4x41Armement et désarmement par code
Buzzer actif (5 V)1Bip des touches, alerte d'entrée et de sortie, sirène intérieure
Module relais 1 voie 5 V1Commande la sirène 12 V isolée de l'Arduino
Sirène 12 V (idéalement avec flash)1Le son d'alarme principal, en général 100–120 dB
LED rouge et verte + 2 résistances de 220 Ω1 lotVoyants armé et prêt
Alimentation 12 V, module LM2596, batterie 12 V (en option)1 lotAlimentation et secours en cas de coupure
Breadboard, fils Dupont, câble d'alarme 4 conducteurs-Connexions et capteurs éloignés
Arduino Uno
Arduino Uno
Reed
Reed
SW-420
SW-420
KY-008
KY-008
Keypad 4x4
Keypad 4x4
Buzzer
Buzzer
Relay 5 V
Relay 5 V

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.
Le KY-008 est un laser de classe 3R ; regarder le faisceau directement ou via un miroir peut abîmer tes yeux. Garde toujours le faisceau sous la hauteur des yeux, par exemple à 20–40 cm du sol. Cette hauteur est sûre et garantit aussi que toute personne qui entre coupe le faisceau en marchant.

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.

ConnexionArduino UnoESP32Raspberry 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 / GND5V / D10 / GNDVIN (5V) / GPIO 27 / GNDVBUS / GP16 / GND
Contact reed (2 fils)D11 et GNDGPIO 33 et GNDGP15 et GND
SW-420 VCC / DO / GND5V / D12 / GND3V3 / GPIO 32 / GND3V3(OUT) / GP17 / GND
KY-008 S / − (laser toujours allumé)5V / GND––
LDR entre 5V et A0, 10 kΩ entre A0 et GNDA0––
Buzzer actif + (− à GND)A1GPIO 25GP14
IN du module relaisA2GPIO 26GP13
LED verte (avec 220 Ω) : prêtA3––
LED rouge (avec 220 Ω) : arméD13GPIO 2 (LED intégrée)LED intégrée
Ne branche jamais la sirène 12 V directement sur une broche de l'Arduino. Le positif de l'alimentation va sur la borne COM du relais, la borne NO du relais sur le positif de la sirène, et le négatif de la sirène revient au négatif de l'alimentation ; ainsi, le courant de la sirène ne traverse jamais l'Arduino. Les broches 3,3 V de l'ESP32 et du Pico n'arrivent pas toujours à relâcher certains modules relais 5 V, et le relais peut rester collé ; sur ces cartes, utilise un module compatible 3,3 V ou à déclenchement par niveau haut, ou un étage à transistor NPN.

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

  1. 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.
  2. 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.
  3. 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.
  4. Branche les deux fils du contact reed sur D11 et GND, et le SW-420 sur 5V, D12 et GND.
  5. 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.
  6. 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 Ω.
  7. 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.
  8. 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.

C++ (Arduino IDE)
1// Alarm sensör testi: tüm girişleri saniyede iki kez yazdırır / Alarm sensor test: prints every input twice a second
2const int PIR_PIN = 10;        // HC-SR501 OUT
3const int KAPI_PIN = 11;       // reed kapı kontağı, diğer ucu GND / reed door contact, other leg to GND
4const int TITRESIM_PIN = 12;   // SW-420 DO
5const int LAZER_PIN = A0;      // LDR bölücünün orta noktası / LDR divider midpoint
6
7int titresimSayisi = 0;        // yarım saniyedeki darbe sayısı / pulses in the last half second
8int oncekiTitresim = LOW;
9unsigned long sonYazma = 0;
10
11void setup() {
12  Serial.begin(9600);
13  pinMode(PIR_PIN, INPUT);
14  pinMode(KAPI_PIN, INPUT_PULLUP);   // kapı kapalı = LOW, açık = HIGH / door closed = LOW, open = HIGH
15  pinMode(TITRESIM_PIN, INPUT);
16}
17
18void loop() {
19  int t = digitalRead(TITRESIM_PIN);  // kısa darbeleri kaçırmamak için sürekli oku / read constantly so short pulses are not missed
20  if (t == HIGH && oncekiTitresim == LOW) titresimSayisi++;
21  oncekiTitresim = t;
22
23  if (millis() - sonYazma >= 500) {
24    sonYazma = millis();
25    Serial.print(F("PIR: "));
26    Serial.print(digitalRead(PIR_PIN));
27    Serial.print(F("  Kapi / door: "));
28    Serial.print(digitalRead(KAPI_PIN) == HIGH ? F("ACIK / open") : F("kapali / closed"));
29    Serial.print(F("  Titresim / vibration: "));
30    Serial.print(titresimSayisi);
31    Serial.print(F("  Lazer / laser: "));
32    Serial.println(analogRead(LAZER_PIN));
33    titresimSayisi = 0;
34  }
35}
Ce test te donne aussi le seuil du laser : note la valeur quand le faisceau touche la LDR (par ex. 950) et celle avec ta main dans le faisceau (par ex. 250), puis mets le milieu (600) dans LAZER_ESIK du code de l'alarme. Si l'écart entre les deux est inférieur à 300, allonge le tube ou réaligne le laser.

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.

ÉtatQuand 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êtCode + A ou code + B
TEMPO DE SORTIECode + A (total) ou B (partiel)Décompte 30 s ; le buzzer bipe chaque seconde, plus vite les 10 dernièresARMÉ à la fin ; DÉSARMÉ avec code + #
ARMÉÀ la fin de la temporisation de sortieSurveille toutes les zones ; la LED rouge reste alluméeENTRÉE si la porte s'ouvre ; ALARME sur zone immédiate ou intérieure
TEMPO D'ENTRÉEPorte ouverte en mode arméLe buzzer bipe pendant 20 s ; PIR et laser patiententDÉSARMÉ avec code + # ; ALARME à la fin du délai
ALARMEUne zone se déclencheSirène et buzzer sonnent ; la sirène s'arrête après 3 min, la LED rouge clignote viteDÉSARMÉ uniquement avec code + #

Armer, désarmer et le code au clavier

TouchesFonction
2580 + AMode total : les quatre zones actives, 30 s de temporisation de sortie
2580 + BMode 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 CEfface un code mal tapé
DTest des zones : affiche les valeurs de la porte, du PIR et du laser dans le moniteur série
3 codes fauxLe 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.

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

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.

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

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.

Le Wi-Fi n'est pas toujours fiable : en cas de coupure de courant, la box s'éteint aussi, et un cambrioleur peut simplement la débrancher. Brancher la box sur un petit onduleur ou envoyer des SMS comme canal de secours avec un module GSM SIM800L garantit que l'alerte passe quoi qu'il arrive. Si tu veux aussi une photo lors d'une détection de mouvement, consulte envoyer des photos sur Telegram avec l'ESP32-CAM.

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.

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

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.
Avant de te fier au système, fais un test de marche : débranche la sirène de la borne COM du relais, arme l'alarme et parcours le chemin qu'emprunterait un cambrioleur chez toi. Observe dans le moniteur série quelle zone se déclenche et à quel moment, puis fais tourner le système quelques jours avec le buzzer seul pour repérer d'éventuels déclenchements intempestifs.

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.

Voir tous les projets
Système d'Alarme de Mouvement ESP32 (PIR + Buzzer + LED + Telegram)
#145Avancé
Premium

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

0.0(0)
9
Système de sécurité laser
#23Avancé
Premium

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

4.8(60)
77
Système d'alerte laser
#22Intermédiaire
Premium

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

4.8(64)
83
Capteur de porte avec interrupteur reed
#38Débutant
Premium

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

4.7(34)
44
Serrure à code avec clavier 4x4 et écran LCD
#64Avancé
Premium

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

4.8(59)
75
Détecteur de mouvement avec capteur de mouvement
#24Débutant
Premium

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

4.6(47)
67
Coffre-fort électronique intelligent
#92Avancé
Premium

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

4.7(42)
63