Antifurto con Arduino: PIR, sensore porta, tastierino e sirena
Come costruire un antifurto con Arduino: PIR, contatto magnetico, sensori di vibrazione e laser, codice da tastierino, ritardo di ingresso, sirena e Telegram.
Un antifurto con Arduino è un sistema di sicurezza che puoi costruire da solo: fa suonare una sirena quando qualcuno entra senza permesso in casa o nella tua stanza, ti lascia qualche secondo per digitare il codice quando la porta si apre e, se vuoi, ti manda una notifica istantanea sul telefono. In questa guida costruirai un allarme a quattro zone con un sensore di movimento PIR, un contatto reed sulla porta, un sensore di vibrazione che rileva i colpi sul vetro della finestra e una barriera laser che attraversa il corridoio. Lo inserirai e disinserirai con un codice su un tastierino 4x4, comanderai una sirena a 12 V tramite un relè e gestirai tutto con un solo Arduino Uno.
Passo dopo passo realizzeremo una piccola copia di quello che fanno le centrali d'allarme commerciali: ritardo di ingresso e di uscita, inserimento parziale (modalità notte), blocco dopo codici errati, durata limitata della sirena e memoria d'allarme. Invece di blocchi if annidati scriveremo il codice come una macchina a stati, così sarà sempre chiaro che cosa sta facendo l'allarme in ogni momento. Poi porterai la stessa logica su un ESP32 per inserire, disinserire e ricevere gli avvisi via Telegram, scriverai una versione MicroPython per il Raspberry Pi Pico e vedrai come ridurre il problema più fastidioso di tutti: i falsi allarmi.

Come funziona un antifurto con Arduino?
Ogni sistema d'allarme ha tre parti: gli ingressi (i sensori), un cervello che decide (l'Arduino) e le uscite (sirena, buzzer, notifica). Ogni sensore sorveglia una zona. Le centrali professionali classificano le zone in base a come si comportano quando scattano, ed è proprio questa distinzione a separare un buon allarme da un aggeggio fastidioso che suona a ogni apertura della porta. Il nostro sistema avrà quattro zone:
| Zona | Sensore | Tipo di zona | Se scatta con l'allarme inserito |
|---|---|---|---|
| Porta d'ingresso | Contatto reed (MC-38) | Ritardata | Parte un ritardo di ingresso di 20 s; allarme se non si digita il codice |
| Soggiorno, interno | PIR (HC-SR501) | Interna (a seguire) | Allarme immediato; attende se il ritardo di ingresso è in corso; esclusa nell'inserimento parziale |
| Finestra | Vibrazione (SW-420) | Istantanea | Allarme immediato dopo 3 colpi in 3 s |
| Corridoio, scale | Laser + LDR (KY-008) | Interna (a seguire) | Allarme immediato; attende se il ritardo di ingresso è in corso |
| Gas, incendio, manomissione | MQ-2, sensore di fiamma, interruttore della scatola | 24 ore | Allarme anche da disinserito (idea di miglioramento) |
Grazie alla zona ritardata, la sirena non suona appena rientri: la porta si apre, il buzzer emette dei bip per avvisarti e hai 20 secondi per digitare il codice. Una zona interna a seguire resta paziente finché il ritardo di ingresso è in corso, quindi passare davanti al PIR mentre vai dalla porta al tastierino non fa scattare nulla. Ma se qualcuno entra senza aprire la porta, per esempio da una finestra, e passa davanti al PIR, nessun ritardo è partito e l'allarme suona subito. Una zona istantanea scatta sempre immediatamente; non ha senso regalare 20 secondi a chi forza una finestra. All'uscita vale un principio simile: quando inserisci il sistema parte un ritardo di uscita di 30 secondi e uscire dalla porta in quel tempo non fa scattare niente.
Lista dei componenti per l'antifurto con Arduino
| Componente | Q.tà | Funzione |
|---|---|---|
| Arduino Uno (o Nano) | 1 | Il cervello del sistema; i pin di questa guida sono uguali su Uno e Nano |
| Sensore di movimento PIR HC-SR501 | 1 | Rilevamento del movimento interno, 3–7 m, circa 110° |
| Contatto magnetico MC-38 (o reed + magnete) | 1 | Apre il circuito quando una porta o una finestra si apre |
| Modulo sensore di vibrazione SW-420 | 1 | Rileva i colpi sul vetro o sul telaio |
| Modulo laser KY-008 + LDR + resistenza da 10 kΩ | 1 set | Barriera di luce attraverso il corridoio |
| Tastierino a membrana 4x4 | 1 | Inserimento e disinserimento con codice |
| Buzzer attivo (5 V) | 1 | Bip dei tasti, avviso di ingresso e uscita, sirena interna |
| Modulo relè a 1 canale da 5 V | 1 | Comanda la sirena a 12 V isolandola dall'Arduino |
| Sirena a 12 V (meglio con lampeggiante) | 1 | Il suono d'allarme principale, di solito 100–120 dB |
| LED rosso e verde + 2 resistenze da 220 Ω | 1 set | Spie di inserito e pronto |
| Alimentatore 12 V, modulo LM2596, batteria 12 V (opzionale) | 1 set | Alimentazione e riserva durante i blackout |
| Breadboard, cavetti jumper, cavo per allarmi a 4 poli | - | Collegamenti e sensori lontani |







I sensori: che cosa rileva ogni zona?
- PIR (HC-SR501): Rileva le variazioni della radiazione infrarossa emessa dal corpo umano e vede il movimento a 3–7 metri con un angolo di circa 110 gradi. La sua uscita è a 3,3 V, ma va alimentato a 5 V. Nei 30–60 secondi dopo l'accensione impara l'ambiente e può scattare da solo; per questo il codice ignora il primo minuto. Trovi i dettagli nell'articolo sensore di movimento PIR.
- Contatto magnetico per porta (MC-38): Due corpi in plastica, uno avvitato al telaio e uno all'anta. Con la porta chiusa il magnete è vicino e il contatto è chiuso; quando la porta si apre di oltre 15–25 mm il contatto si apre. Si legge con un solo pin e il pull-up interno; spieghiamo cosa c'è dentro nell'articolo reed switch.
- Sensore di vibrazione (SW-420): Un interruttore normalmente chiuso con un'asta metallica circondata da una molla. Quando il vetro o il telaio ricevono un colpo, il contatto si apre per un istante e il comparatore LM393 sulla scheda lo trasforma in un impulso digitale. Nella maggior parte dei moduli l'uscita è LOW a riposo e diventa HIGH per un attimo a ogni colpo; verifica il tuo modulo con il test dei sensori. La sensibilità si regola con il trimmer; i dettagli sono nell'articolo sensore di vibrazione SW-420.
- Barriera laser (KY-008 + LDR): Un laser da 650 nm e circa 5 mW colpisce di continuo una LDR dall'altra parte del corridoio. Quando il raggio viene interrotto, la resistenza della LDR sale da qualche centinaio di ohm a decine di kiloohm e la lettura analogica cala nettamente. Consigli di montaggio e allineamento nell'articolo modulo laser.
- Tastierino 4x4: Una matrice di 16 tasti formata da 4 linee di riga e 4 di colonna; usa 8 pin e si legge in una riga con la libreria Keypad. Il funzionamento della scansione righe-colonne è spiegato nella guida al tastierino.
- Sirena e buzzer: Un buzzer attivo ha un oscillatore interno e suona appena porti il pin a HIGH; è perfetto per il bip dei tasti e gli avvisi di ingresso e uscita. Il suono davvero dissuasivo arriva dalla sirena a 12 V, che comandi tramite un modulo relè. La differenza tra buzzer attivo e passivo è spiegata in cos'è un buzzer.
Schema di collegamento e tabella dei pin
La tabella riunisce i collegamenti delle tre schede. La versione per Arduino Uno usa tutte e quattro le zone e il tastierino. Poiché la versione ESP32 si inserisce e disinserisce con comandi Telegram invece del tastierino e la versione Pico è volutamente breve, la barriera laser c'è solo sull'Uno. Per vedere i pin della tua scheda usa lo strumento Pinout, e per calcolare le resistenze dei LED il calcolatore di resistenza per LED.
| Collegamento | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| Tastierino R1–R4 (righe) | D9, D8, D7, D6 | – (Telegram) | GP2, GP3, GP4, GP5 |
| Tastierino C1–C4 (colonne) | D5, D4, D3, D2 | – (Telegram) | GP6, GP7, GP8, GP9 |
| PIR VCC / OUT / GND | 5V / D10 / GND | VIN (5V) / GPIO 27 / GND | VBUS / GP16 / GND |
| Contatto reed (2 fili) | D11 e GND | GPIO 33 e GND | GP15 e GND |
| SW-420 VCC / DO / GND | 5V / D12 / GND | 3V3 / GPIO 32 / GND | 3V3(OUT) / GP17 / GND |
| KY-008 S / − (laser sempre acceso) | 5V / GND | – | – |
| LDR tra 5V e A0, 10 kΩ tra A0 e GND | A0 | – | – |
| Buzzer attivo + (− a GND) | A1 | GPIO 25 | GP14 |
| IN del modulo relè | A2 | GPIO 26 | GP13 |
| LED verde (con 220 Ω): pronto | A3 | – | – |
| LED rosso (con 220 Ω): inserito | D13 | GPIO 2 (LED integrato) | LED integrato |
Un allarme serve soprattutto quando manca la corrente, quindi pensa all'alimentazione fin dall'inizio. Una soluzione pratica: come nelle centrali professionali, un alimentatore per allarmi da 13,8 V con collegamento alla batteria alimenta la sirena e, ridotto a 5 V con un convertitore step-down LM2596, il pin 5V dell'Arduino. Se manca la rete, subentra una batteria al piombo da 12 V e 7 Ah. Il calcolo è semplice: Uno, PIR, laser, LED e SW-420 assorbono circa 100 mA a 5 V a riposo; sono 0,5 W, cioè circa 50 mA lato batteria con un convertitore all'85 % di rendimento. Anche usando solo l'80 % della capacità, una batteria da 7 Ah tiene in funzione il sistema per 4–5 giorni; gli 0,2–0,5 A della sirena mentre suona riducono un po' questo tempo. Prova i tuoi valori con il calcolatore di durata della batteria e leggi come alimentare Arduino per le opzioni di alimentazione.
Montaggio passo dopo passo
- Nell'Arduino IDE apri il Gestore librerie, scrivi Keypad e installa la libreria di Mark Stanley e Alexander Brevig. Senza di essa, in compilazione otterrai l'errore Keypad.h: No such file or directory.
- Tieni il tastierino con i tasti rivolti verso di te; collega i primi quattro fili da sinistra (R1–R4) a D9, D8, D7, D6 e i quattro successivi (C1–C4) a D5, D4, D3, D2.
- Collega il PIR a 5V, D10 e GND; ruota entrambi i trimmer completamente in senso antiorario e metti il ponticello di trigger su H.
- Collega i due fili del contatto reed a D11 e GND, e il SW-420 a 5V, D12 e GND.
- Metti la LDR tra 5V e A0 e la resistenza da 10 kΩ tra A0 e GND; inserisci la LDR in fondo a un tubetto nero e punta il laser al centro del tubetto.
- Collega il buzzer ad A1, l'IN del modulo relè ad A2 e i suoi VCC e GND ai 5V e GND dell'Arduino; collega i LED ad A3 e D13 con resistenze da 220 Ω.
- Carica prima il test dei sensori qui sotto, apri il Monitor seriale a 9600 baud e attiva i sensori uno alla volta.
- Poi carica il codice completo dell'allarme, cambia il codice di accesso e prova il sistema per qualche giorno solo con il buzzer prima di collegare la sirena.
Primo test: vedere tutti i sensori nel Monitor seriale
Prima di passare al codice dell'allarme, assicurati che ogni sensore si comporti come ti aspetti. Questo piccolo sketch stampa due volte al secondo lo stato del PIR, se la porta è aperta o chiusa, il numero di impulsi di vibrazione dell'ultimo mezzo secondo e il valore analogico della LDR. Il pin della vibrazione viene letto di continuo, perché gli impulsi del SW-420 possono durare pochi millisecondi e un codice con delay() tra una lettura e l'altra li perderebbe.
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}Macchina a stati: il cervello dell'allarme
Se provi a scrivere un allarme con blocchi if e delay(), diventa presto un groviglio: mentre si aspetta il ritardo di ingresso nessuno legge il tastierino, e mentre suona la sirena non si può digitare il codice. La soluzione è una macchina a stati, in cui il sistema si trova sempre in un solo stato e passa a un altro solo in presenza di eventi precisi. Nel codice gli stati sono definiti con un enum, loop() applica a ogni giro solo le regole dello stato attuale e le attese si misurano con millis() invece di delay(). Così il ciclo gira migliaia di volte al secondo e non si perde nessuna pressione di tasto né alcun evento dei sensori.
| Stato | Quando si entra? | Che cosa fa? | Quando si esce? |
|---|---|---|---|
| DISINSERITO | All'accensione e dopo codice + # | Osserva i sensori ma non dà mai allarme; il LED verde si accende se tutto è pronto | Codice + A o codice + B |
| RITARDO DI USCITA | Codice + A (totale) o B (parziale) | Conta 30 s; il buzzer suona ogni secondo e accelera negli ultimi 10 s | INSERITO allo scadere; DISINSERITO con codice + # |
| INSERITO | Allo scadere del ritardo di uscita | Sorveglia tutte le zone; il LED rosso resta acceso | INGRESSO se si apre la porta; ALLARME su zona istantanea o interna |
| RITARDO DI INGRESSO | Porta aperta con allarme inserito | Il buzzer suona a intermittenza per 20 s; PIR e laser attendono | DISINSERITO con codice + #; ALLARME allo scadere |
| ALLARME | Una zona è scattata | Suonano sirena e buzzer; la sirena tace dopo 3 min, il LED rosso lampeggia veloce | Solo DISINSERITO con codice + # |
Inserire, disinserire e il codice sul tastierino
| Tasti | Funzione |
|---|---|
| 2580 + A | Inserimento totale: tutte e quattro le zone attive, 30 s di ritardo di uscita |
| 2580 + B | Inserimento parziale (notte): PIR escluso; porta, finestra e laser attivi |
| 2580 + # | Disinserisce; se c'è stato un allarme, mostra nel Monitor seriale la zona che è scattata |
| * o C | Cancella un codice digitato male |
| D | Test delle zone: stampa nel Monitor seriale i valori di porta, PIR e laser |
| 3 codici errati | Il tastierino si blocca per 60 s; in quel tempo viene rifiutato anche il codice giusto |
Chiedere il codice anche per inserire impedisce a bambini o ospiti di attivare il sistema per errore. Il codice esegue anche un controllo di non pronto: se provi a inserire con la porta aperta o il raggio laser interrotto, senti un bip lungo e il sistema non si inserisce, proprio come nelle centrali commerciali. Non lasciare il codice predefinito 2580 ed evita combinazioni facili come 1234, 0000 o il tuo anno di nascita. Se vuoi cambiare il codice senza ricaricare lo sketch, salvalo nella EEPROM e aggiungi un menu che ti permetta di digitarne uno nuovo dal tastierino. Con un display LCD I2C accanto al tastierino vedrai anche il conto alla rovescia e i messaggi di stato; per questo i pin A4 e A5 sono rimasti liberi.
Codice dell'antifurto con Arduino
Il codice qui sotto è il cuore di questa guida. Nella sezione delle impostazioni in alto puoi cambiare il codice di accesso, i ritardi, la durata della sirena, la soglia del laser e il tipo di relè; il resto non va toccato. Su un Arduino Uno occupa circa 6 KB di memoria programma e 620 byte di RAM, quindi resta molto spazio per aggiungere un LCD o un lettore 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 spina dorsale del codice è la funzione suz(). Ogni volta che il valore grezzo di un ingresso cambia, viene preso un timestamp, e lo stato stabile non cambia finché il valore non resta uguale per un tempo fisso (50 ms per la porta, 200 ms per il PIR, 30 ms per il laser). Questo semplice filtro evita che brevi disturbi sui cavi lunghi e i rimbalzi dei contatti facciano scattare l'allarme. Anche leggere il pin della porta con INPUT_PULLUP è una scelta voluta: con la porta chiusa il reed collega il pin a GND e si legge LOW; quando la porta si apre o qualcuno taglia il cavo, il pin passa a HIGH. Questa logica a circuito chiuso degli allarmi veri tratta un cavo tagliato esattamente come una porta aperta.
Per la vibrazione si usa un metodo diverso. Anche un solo colpo fa rimbalzare il SW-420 più volte in pochi millisecondi; titresimVar() conta prima come un unico evento tutto ciò che avviene entro 150 ms, poi memorizza gli istanti degli ultimi tre eventi. Se tre eventi stanno in 3 secondi, si considera un vero tentativo di forzare la finestra; il vento che scuote la porta o un pallone contro il vetro non fanno scattare nulla. La sirena tace da sola dopo 3 minuti, ma il sistema resta nello stato ALLARME e il LED rosso lampeggia veloce per avvisare chi rientra che c'è stato un allarme. Quando digiti il codice giusto, il nome della zona salvato nella variabile tetikleyen compare nel Monitor seriale: è una piccola versione della memoria d'allarme delle centrali commerciali.
Come regolare i ritardi di ingresso e di uscita
Se i ritardi sono troppo brevi farai scattare il tuo stesso allarme; se sono troppo lunghi regalerai tempo al ladro. Scegli un ritardo di uscita pari al doppio del tempo che ti serve per andare dal tastierino alla porta e chiuderla a chiave, e un ritardo di ingresso pari a circa il doppio del tempo per aprire la porta, raggiungere il tastierino e digitare il codice. Le centrali commerciali usano di solito 30–60 secondi per l'uscita e 15–30 secondi per l'ingresso. Montare il tastierino accanto alla porta, ma non visibile dall'esterno, ti permette di tenere breve il ritardo di ingresso. Conta anche il comportamento del buzzer: in uscita un bip breve ogni secondo accelera negli ultimi 10 secondi per farti sentire che il tempo sta finendo; in ingresso un bip intermittente da 200 ms ti ricorda di digitare il codice.
Collegare la sirena e il relè
Nel circuito della sirena l'Arduino comanda solo la bobina del relè; la corrente a 12 V della sirena passa dai contatti del relè. La maggior parte dei moduli relè è attiva bassa, cioè si eccita quando il pin IN è LOW; per questo nel codice c'è la costante ROLE_AKTIF_LOW. Il codice scrive il livello di spento prima di configurare il pin come uscita, così all'avvio dell'Arduino il relè non scatta per un istante e la sirena non emette un breve trillo. Quando scegli la sirena guarda l'assorbimento: le piccole sirene a 12 V da 110 dB assorbono di solito 0,1–0,3 A, un carico minimo per un contatto da 10 A. Una sirena esterna con lampeggiante dissuade con suono e luce e mostra ai vicini da dove arriva l'allarme. Le centrali professionali di solito spengono la sirena dopo 3 minuti e in alcuni paesi esiste anche un limite di legge, quindi tieni SIREN_SURE su un valore ragionevole. Se preferisci un interruttore a stato solido al posto del relè, puoi usare un MOSFET logic level e, per le sirene a bobina (elettromagnetiche), aggiungere un diodo in inversa ai capi dei morsetti.
Avvisi Telegram con l'ESP32
Una sirena che nessuno sente non serve a niente; il modo più semplice per sapere che l'allarme è scattato mentre sei fuori è un ESP32 con Telegram. L'ESP32 si collega al Wi-Fi e invia messaggi al server di Telegram con una connessione cifrata, e tu puoi anche mandargli dei comandi. Abbiamo spiegato passo dopo passo come creare un bot con BotFather, ottenere il token e il tuo chat ID e installare le librerie UniversalTelegramBot e ArduinoJson nell'articolo bot Telegram con ESP32. Questa versione non ha il tastierino; si comanda dal telefono con i comandi /kur (inserimento totale), /evde (inserimento parziale), /coz (disinserire) e /durum (stato). Il bot risponde solo al tuo chat ID e ignora in silenzio i comandi di chiunque altro.
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}Il codice per ESP32 ha tre differenze importanti. La prima: inviare un messaggio a Telegram o chiedere i nuovi comandi può richiedere 1–2 secondi, e intanto loop() aspetta. Una porta che resta aperta e un PIR che resta HIGH per almeno qualche secondo non ne risentono, ma gli impulsi di pochi millisecondi del SW-420 potrebbero andare persi; per questo la vibrazione viene catturata con un interrupt (attachInterrupt). La seconda: alarmVer() accende prima la sirena e solo dopo invia il messaggio, quindi per quanto lenta sia la connessione la sirena non è mai in ritardo. Le notifiche non inviate finiscono in una coda e vengono ritentate ogni 15 secondi. La terza: la libreria Preferences salva nella memoria flash se il sistema è inserito; se la corrente salta e torna mentre è inserito, l'ESP32 riparte inserito e ti avvisa che la corrente è tornata. Se relè e sirena condividono con l'ESP32 un alimentatore debole, la scheda può riavviarsi con l'errore Brownout detector was triggered; la soluzione è nell'articolo l'ESP32 si riavvia di continuo.
Allarme con Raspberry Pi Pico e MicroPython
Puoi realizzare la stessa macchina a stati su un Raspberry Pi Pico con un breve programma MicroPython. Il tastierino si legge con la funzione di scansione righe-colonne della guida al tastierino; le righe sono su GP2–GP5 e le colonne, con pull-up interno, su GP6–GP9. Alimenta il PIR da VBUS (5 V); la sua uscita è già a 3,3 V, quindi va direttamente su GP16. Il SW-420 invece va alimentato sempre dal pin 3V3(OUT), perché l'uscita di un modulo alimentato a 5 V può danneggiare l'ingresso del Pico. Gli impulsi di vibrazione vengono catturati con Pin.irq e i loro istanti salvati in una lista, mentre gli stati sono gestiti con semplici stringhe. I tempi si calcolano con time.ticks_diff(), perché questa funzione dà la differenza corretta anche quando il contatore ricomincia da capo.
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)Come ridurre i falsi allarmi
Il peggior nemico di un sistema d'allarme non è il ladro, ma il falso allarme: una sirena che ha suonato tre volte per niente non la prende più sul serio nessuno, né tu né i vicini. Una parte delle misure seguenti è già nel codice; il resto riguarda montaggio e cablaggio:
- Aspetta il riscaldamento: Il PIR può scattare da solo nei primi 30–60 secondi dopo l'accensione; il codice ignora il primo minuto.
- Usa un filtro: Aspettare che ogni ingresso resti stabile per un certo tempo (antirimbalzo) elimina i brevi disturbi sui cavi.
- Conta i colpi: Cercare 3 colpi in 3 secondi invece di uno solo scarta il vento, i camion che passano e le porte sbattute. Se serve, porta il conteggio a 4–5 o abbassa la sensibilità con il trimmer.
- Posiziona bene il PIR: Non puntarlo verso termosifoni, condizionatori, finestre soleggiate o tende; rileva meglio quando il movimento attraversa il campo visivo di lato. Se hai animali, montalo in alto, inclinalo un po' verso il basso e abbassa la sensibilità.
- Richiedi due sensori: Nelle zone critiche, pretendere che PIR e laser, o PIR e porta, scattino a pochi secondi di distanza (zone incrociate) impedisce che un singolo sensore difettoso provochi un allarme.
- Pulisci l'alimentazione: Il calo di tensione quando il relè si eccita può far scattare il PIR. Metti condensatori da 100 µF e 100 nF tra VCC e GND di ogni sensore e fai passare i cavi di relè e sirena lontano da quelli dei sensori.
- Usa pull-up esterne sui cavi lunghi: La pull-up interna dell'Arduino è di 20–50 kΩ e raccoglie disturbi su 10 metri di cavo; una resistenza esterna da 4,7 kΩ e un condensatore da 100 nF sul pin rendono la linea molto più robusta.
- Proteggi il laser dalla luce ambiente: Metti la LDR dentro un tubetto nero e prova la soglia sia di giorno sia di notte.
Errori comuni e soluzioni
- L'allarme scatta appena inserito: Il PIR si sta ancora scaldando o il contatto della porta è collegato male. Nel test dei sensori la porta chiusa deve leggere LOW; se legge HIGH, il magnete è troppo lontano o un filo è interrotto.
- Il tastierino non risponde o legge il tasto sbagliato: I fili di righe e colonne sono invertiti. Invece di ricablare, cambia l'ordine degli array satirPinleri e sutunPinleri nel codice.
- Si sente un bip lungo quando provi a inserire: Non è un guasto ma l'avviso di non pronto: la porta è aperta o il raggio laser non arriva alla LDR. Premi D per vedere lo stato delle zone.
- Il relè scatta per un attimo all'accensione: Il livello di spento non è stato scritto prima di impostare il pin come uscita, oppure ROLE_AKTIF_LOW è rimasto true con un relè attivo alto.
- L'Arduino si resetta quando suona la sirena: Sirena e Arduino condividono un alimentatore debole e il calo di tensione riavvia la scheda. Usa un alimentatore più potente, un convertitore separato e cavi più spessi; dettagli in Arduino si resetta da solo.
- Il sensore di vibrazione non scatta mai o scatta sempre: Ruota lentamente il trimmer per regolare la soglia e incolla bene il sensore al vetro o al telaio; un sensore fissato male non trasmette il colpo.
- L'ESP32 non invia messaggi: Il token o il chat ID sono sbagliati, la rete è a 5 GHz o non hai mai avviato il bot su Telegram. Un bot non può scriverti finché non gli hai mandato /start almeno una volta.
- Il laser va bene di giorno ma di notte dà falsi allarmi (o il contrario): La soglia è troppo vicina al livello di luce ambiente. Misura i valori con e senza raggio in entrambe le condizioni e scegli un nuovo LAZER_ESIK a metà.
Idee per migliorare il tuo antifurto
- Inserire e disinserire con una tessera: Aggiungi un lettore RFID RC522 accanto al tastierino e disinserisci con un portachiavi.
- Zone 24 ore: Con un sensore di gas MQ-2 o un sensore di fiamma crea una zona gas e incendio che dà l'allarme anche a sistema disinserito.
- Contatti porta senza fili: Realizza contatti a batteria per le finestre lontane con moduli RF a 433 MHz.
- Pannello con display: Mostra conto alla rovescia, stato e memoria d'allarme su un LCD I2C.
- Integrazione domotica: Collega l'ESP32 a MQTT e Home Assistant e gestisci l'allarme insieme a luci e telecamere.
- Contatto antimanomissione (tamper): Metti un microinterruttore sul coperchio della scatola, così il buzzer suona se qualcuno la apre, anche a sistema disinserito.
- Progetto con plastico: Riunisci tutte queste zone in una casa in miniatura come livello di sicurezza di un progetto di casa intelligente.
Poiché unisce lettura dei sensori, gestione dei tempi, macchina a stati e progettazione dell'alimentazione in un solo progetto, un antifurto con Arduino è anche un ottimo modo per imparare. Inizia dal test dei sensori, prova poi l'allarme per qualche giorno solo con il buzzer e collega la sirena per ultima. Nell'app BlueGrays trovi progetti come il sensore porta con reed, il sistema di sicurezza laser, la serratura a codice con tastierino 4x4 e LCD e il sistema d'allarme di movimento ESP32 con avvisi Telegram, ognuno con schemi e codice passo dopo passo.
Domande frequenti
Come si fa un antifurto con Arduino?
Colleghi a un Arduino Uno un sensore di movimento PIR, un contatto reed per la porta, un tastierino, un buzzer attivo e un modulo relè che comanda una sirena. Il codice è una macchina a stati che porta il sistema tra disinserito, ritardo di uscita, inserito, ritardo di ingresso e allarme. Si inserisce e disinserisce con un codice sul tastierino e, se un sensore scatta con l'allarme inserito, suona la sirena.
Quali sensori servono per un allarme con Arduino?
I più usati sono un sensore di movimento PIR (HC-SR501) per gli interni, contatti magnetici reed (MC-38) per porte e finestre, un sensore di vibrazione SW-420 per i colpi sui vetri e una barriera laser per i corridoi. Si possono aggiungere un MQ-2 per il gas e un sensore di fiamma per gli incendi. Ogni sensore è definito come una zona separata con un proprio comportamento.
Un antifurto con Arduino funziona se manca la corrente?
No, se è alimentato solo da un alimentatore da rete. Con un alimentatore per allarmi da 13,8 V con batteria e una batteria da 12 V e 7 Ah, un sistema con Uno che assorbe circa 100 mA funziona per 4–5 giorni. Nella versione ESP32 lo stato inserito viene salvato in flash, quindi al ritorno della corrente il sistema riparte inserito; perché gli avvisi partano, anche il router deve essere sotto UPS.
Un allarme con Arduino può mandare notifiche al telefono?
Un Arduino Uno da solo non può, ma un ESP32 invia messaggi Telegram istantanei via Wi-Fi e riceve dal telefono comandi come /kur e /coz. Dove non c'è internet, un modulo GSM SIM800L può inviare SMS o fare una chiamata. Usare entrambi i metodi insieme è la soluzione più affidabile.
Perché il sensore PIR dà falsi allarmi?
Le cause più comuni sono il riscaldamento di 30–60 secondi dopo l'accensione, fonti di calore come termosifoni e condizionatori, tende che si muovono scaldate dal sole e i disturbi sull'alimentazione quando commuta un relè. Ignorare il riscaldamento nel codice, tenere il sensore lontano dalle fonti di calore e mettere un condensatore tra VCC e GND risolve la maggior parte dei casi.
Un antifurto con Arduino si può usare in una casa vera?
Funziona bene per imparare e per sorvegliare una stanza, un garage o una cantina, ma non sostituisce un sistema d'allarme certificato. I sistemi commerciali sono collaudati secondo norme come la EN 50131 per antimanomissione, alimentazione di riserva, comunicazioni radio cifrate e collegamento a una centrale di vigilanza. Se usi un sistema con Arduino in casa, ti consigliamo di aggiungere una batteria di riserva, un contatto antimanomissione e un secondo canale di avviso.
Progetti da realizzare dopo questo articolo
Vedi tutti i progetti
Sistema di Allarme Movimento ESP32 (PIR + Buzzer + LED + Telegram)
Un sistema di allarme di sicurezza intelligente che attiva buzzer e LED quando il sensore PIR rileva movimento, inviando contemporaneamente una notifica istantanea al proprietario tramite Telegram. Dispone di un tempo di attesa di 30 secondi per limitare la frequenza degli allarmi ripetuti; questo meccanismo non verifica la validità dei rilevamenti del sensore PIR né elimina le false attivazioni. Adatto a sicurezza domestica, monitoraggio di garage, magazzini o studi. Con l'ESP32-CAM è possibile

Sistema di sicurezza laser
Come nelle scene di furti con i laser dei film: un raggio laser colpisce un sensore di luce, e appena qualcuno ci passa in mezzo e interrompe il raggio, si accende il LED rosso e il buzzer comincia a

Sistema di allarme laser
Costruisci una barriera di raggi invisibile, proprio come nei film. Un laser punta su un sensore di luce dall'altra parte; appena qualcuno passa e interrompe il raggio, il LED si accende e il buzzer s

Sensore porta con contatto reed
Capisci se la tua porta è aperta o chiusa guardando una luce. Metti un piccolo magnete sulla porta e sul telaio un interruttore che sente i magneti; a porta chiusa stanno vicini e si accende il LED ve

Serratura con password, tastierino 4x4 e LCD
Una piccola serratura elettronica che non fa entrare chi non conosce il codice. Premi il tasto cancelletto e digiti il codice sul tastierino; se è giusto, lo schermo indica accesso consentito, si acce

Rilevatore di movimento con sensore di movimento
Un semplice guardiano che ti avvisa quando qualcuno entra nella stanza. Il sensore di movimento percepisce il calore emesso dal corpo umano, e l'Arduino tiene acceso il LED finché c'è movimento e lo s

Cassaforte elettronica intelligente
Una piccola cassaforte per le tue cose di valore che si apre con un codice. Digiti il tuo codice a quattro cifre sul tastierino; se è giusto, il servomotore apre la serratura, si accende la luce verde
Altri articoli
App BlueGrays
Costruisci questo circuito passo passo sul telefono
Schema, codice ed elenco componenti in un’unica schermata. Oltre 200 progetti Arduino, ESP32 e Pico da realizzare.
Inquadra con il telefono
Punta la fotocamera sul codice: si apre lo store del tuo telefono.
oppure apri lo store

