Sistema de alarme com Arduino: guia de PIR, sensor de porta, teclado e sirene
Monte um sistema de alarme com Arduino usando PIR, sensor de porta reed, zonas de vibração e laser, senha no teclado, retardo de entrada e saída, relé para sirene e avisos pelo Telegram com ESP32.
Um sistema de alarme com Arduino é um dispositivo de segurança que você mesmo pode montar: ele dispara uma sirene quando alguém entra na sua casa ou no seu quarto sem permissão, dá alguns segundos para você digitar a senha quando a porta abre e, se você quiser, envia uma notificação instantânea para o seu celular. Neste guia você vai montar um alarme de quatro zonas com um sensor de movimento PIR, um contato reed na porta, um sensor de vibração que detecta batidas no vidro da janela e uma barreira a laser atravessando o corredor. Você vai armar e desarmar o sistema com uma senha em um teclado matricial 4x4, acionar uma sirene de 12 V por meio de um relé e controlar tudo com um único Arduino Uno.
Passo a passo, vamos fazer uma pequena cópia do que as centrais de alarme comerciais fazem: retardos de entrada e saída, modo em casa (modo noturno), bloqueio após senhas erradas, limite de tempo da sirene e memória de alarme. Em vez de blocos if aninhados, vamos escrever o código como uma máquina de estados, para que fique sempre claro o que o alarme está fazendo a cada momento. Depois você vai levar a mesma lógica para um ESP32 para armar, desarmar e receber avisos de alarme pelo Telegram, escrever uma versão em MicroPython para o Raspberry Pi Pico e aprender a reduzir o problema mais irritante de todos: os alarmes falsos.

Como funciona um sistema de alarme com Arduino?
Todo sistema de alarme tem três partes: entradas (sensores), um cérebro que toma decisões (o Arduino) e saídas (sirene, buzzer, notificação). Cada sensor protege uma zona. As centrais profissionais classificam as zonas pela forma como se comportam ao serem acionadas, e é essa distinção que separa um bom alarme de um aparelho irritante que dispara toda vez que uma porta abre. Nosso sistema terá quatro zonas:
| Zona | Sensor | Tipo de zona | Se acionada com o sistema armado |
|---|---|---|---|
| Porta de entrada | Contato reed (MC-38) | Temporizada | Inicia um retardo de entrada de 20 s; alarme se nenhuma senha for digitada |
| Sala, área interna | PIR (HC-SR501) | Interna (seguidora) | Alarme imediato; espera se o retardo de entrada estiver correndo; desligada no modo em casa |
| Janela | Vibração (SW-420) | Instantânea | Alarme imediato após 3 batidas em 3 s |
| Corredor, escada | Laser + LDR (KY-008) | Interna (seguidora) | Alarme imediato; espera se o retardo de entrada estiver correndo |
| Gás, incêndio, violação | MQ-2, sensor de chama, chave da caixa | 24 horas | Alarme mesmo com o sistema desarmado (ideia de melhoria) |
Graças à zona temporizada, a sirene não dispara no instante em que você chega em casa: a porta abre, o buzzer bipa para avisar e você tem 20 segundos para digitar a senha. Uma zona interna seguidora (follower) tem paciência enquanto o retardo de entrada está correndo, então passar na frente do PIR no caminho da porta até o teclado não dispara o alarme. Mas, se alguém entrar sem abrir a porta, por exemplo pela janela, e passar na frente do PIR, nenhum retardo de entrada foi iniciado e o alarme toca na hora. Uma zona instantânea sempre dispara imediatamente; não faz sentido dar 20 segundos a quem está forçando uma janela. Na saída funciona de forma parecida: quando você arma o sistema, começa um retardo de saída de 30 segundos, e sair pela porta nesse tempo não dispara nada.
Lista de componentes do sistema de alarme com Arduino
| Componente | Qtd. | Função |
|---|---|---|
| Arduino Uno (ou Nano) | 1 | O cérebro do sistema; os pinos deste guia são os mesmos no Uno e no Nano |
| Sensor de movimento PIR HC-SR501 | 1 | Detecção de movimento em ambientes internos, 3–7 m, cerca de 110° |
| Contato reed de porta MC-38 (ou chave reed + ímã) | 1 | Abre o circuito quando uma porta ou janela é aberta |
| Módulo sensor de vibração SW-420 | 1 | Detecta batidas no vidro ou no batente |
| Módulo laser KY-008 + LDR + resistor de 10 kΩ | 1 conjunto | Barreira de luz atravessando o corredor |
| Teclado matricial de membrana 4x4 | 1 | Armar e desarmar com senha |
| Buzzer ativo (5 V) | 1 | Som das teclas, aviso de entrada e saída, sirene interna |
| Módulo relé de 1 canal 5 V | 1 | Aciona a sirene de 12 V isolada do Arduino |
| Sirene de 12 V (de preferência com estrobo) | 1 | O som principal do alarme, geralmente 100–120 dB |
| LED vermelho e verde + 2 resistores de 220 Ω | 1 conjunto | Indicadores de armado e pronto |
| Fonte de 12 V, módulo LM2596, bateria de 12 V (opcional) | 1 conjunto | Alimentação e reserva durante quedas de energia |
| Protoboard, jumpers, cabo de alarme de 4 vias | - | Ligações e sensores distantes |







Sensores: o que cada zona detecta?
- PIR (HC-SR501): Detecta variações na radiação infravermelha emitida pelo corpo humano e enxerga movimento a 3–7 metros em um ângulo de cerca de 110 graus. Sua saída é de 3,3 V, mas ele precisa de alimentação de 5 V. Nos primeiros 30–60 segundos depois de ligado, ele aprende o ambiente e pode disparar sozinho; por isso o código ignora os primeiros 60 segundos. Veja os detalhes no artigo sobre o sensor de movimento PIR.
- Contato reed de porta (MC-38): São duas caixinhas plásticas, uma parafusada no batente e outra na folha da porta. Com a porta fechada, o ímã fica perto e o contato fica fechado; quando a porta abre mais de 15–25 mm, o contato abre. Ele é lido com um único pino e o pull-up interno; explicamos o que há dentro dele no artigo sobre a chave reed.
- Sensor de vibração (SW-420): Uma chave normalmente fechada com uma haste metálica envolvida por uma mola. Quando o vidro ou o batente recebe uma batida, o contato abre por um instante e o comparador LM393 da placa transforma isso em um pulso digital. Na maioria dos módulos, a saída fica em LOW em repouso e vai para HIGH por um instante a cada batida; confira o seu módulo com o teste de sensores. A sensibilidade é ajustada no trimpot da placa; detalhes no artigo sobre o sensor de vibração SW-420.
- Barreira a laser (KY-008 + LDR): Um laser de 650 nm e cerca de 5 mW ilumina continuamente um LDR do outro lado do corredor. Quando o feixe é interrompido, a resistência do LDR salta de algumas centenas de ohms para dezenas de kiloohms e a leitura analógica cai bruscamente. Dicas de montagem e alinhamento estão no artigo sobre o módulo laser.
- Teclado 4x4: Uma matriz de 16 teclas formada por 4 linhas e 4 colunas; usa 8 pinos e é lido em uma única linha de código com a biblioteca Keypad. Você encontra como funciona a varredura de linhas e colunas no guia do teclado matricial.
- Sirene e buzzer: Um buzzer ativo tem oscilador próprio, então ele apita assim que você coloca o pino em HIGH; é ideal para o som das teclas e os avisos de entrada e saída. O som que realmente afasta intrusos vem da sirene de 12 V, que você aciona por meio de um módulo relé. Para a diferença entre buzzer ativo e passivo, veja o que é um buzzer.
Esquema de ligação e tabela de pinos
A tabela abaixo mostra a ligação das três placas. A versão para Arduino Uno usa as quatro zonas e o teclado. Como a versão para ESP32 é armada e desarmada com comandos do Telegram em vez de teclado, e a versão para Pico foi mantida curta, a barreira a laser existe só no Uno. Para ver os números dos pinos da sua placa, use a ferramenta Pinagem, e para calcular os resistores dos LEDs use a calculadora Resistor para LED.
| Ligação | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| Teclado R1–R4 (linhas) | D9, D8, D7, D6 | – (Telegram) | GP2, GP3, GP4, GP5 |
| Teclado C1–C4 (colunas) | D5, D4, D3, D2 | – (Telegram) | GP6, GP7, GP8, GP9 |
| PIR VCC / OUT / GND | 5V / D10 / GND | VIN (5V) / GPIO 27 / GND | VBUS / GP16 / GND |
| Contato reed (2 fios) | 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 ligado) | 5V / GND | – | – |
| LDR entre 5V e A0, 10 kΩ entre A0 e GND | A0 | – | – |
| Buzzer ativo + (− no GND) | A1 | GPIO 25 | GP14 |
| IN do módulo relé | A2 | GPIO 26 | GP13 |
| LED verde (com 220 Ω): pronto | A3 | – | – |
| LED vermelho (com 220 Ω): armado | D13 | GPIO 2 (LED integrado) | LED integrado |
O alarme é mais necessário justamente quando falta energia, então pense na alimentação desde o começo. Uma montagem prática: assim como nas centrais profissionais, uma fonte de alarme de 13,8 V com ligação para bateria alimenta a sirene e, reduzida para 5 V com um conversor buck LM2596, o pino 5V do Arduino. Quando a rede elétrica cai, uma bateria selada de chumbo-ácido de 12 V 7 Ah assume. A conta é simples: o Uno, o PIR, o laser, os LEDs e o SW-420 consomem cerca de 100 mA em 5 V em espera; isso dá 0,5 W, o que, com um conversor de 85% de eficiência, corresponde a uns 50 mA no lado da bateria. Mesmo usando só 80% da capacidade, uma bateria de 7 Ah mantém o sistema funcionando por 4–5 dias; os 0,2–0,5 A que a sirene consome enquanto toca encurtam um pouco esse tempo. Teste os seus próprios valores com a calculadora Autonomia da bateria e veja como alimentar o Arduino para conhecer as opções de alimentação.
Montagem passo a passo
- Na Arduino IDE, abra o Gerenciador de Bibliotecas, digite Keypad e instale a biblioteca de Mark Stanley e Alexander Brevig. Sem ela, você recebe o erro Keypad.h: No such file or directory ao compilar.
- Segure o teclado com as teclas viradas para você; ligue os quatro primeiros fios da esquerda (R1–R4) em D9, D8, D7, D6 e os quatro seguintes (C1–C4) em D5, D4, D3, D2.
- Ligue o PIR em 5V, D10 e GND; gire os dois trimpots totalmente no sentido anti-horário e coloque o jumper de disparo na posição H.
- Ligue os dois fios do contato reed em D11 e GND, e o SW-420 em 5V, D12 e GND.
- Coloque o LDR entre 5V e A0 e o resistor de 10 kΩ entre A0 e GND; posicione o LDR no fundo de um tubo preto e aponte o laser para o centro do tubo.
- Ligue o buzzer em A1, o IN do módulo relé em A2 e o VCC e o GND do módulo no 5V e no GND do Arduino; ligue os LEDs em A3 e D13 com resistores de 220 Ω.
- Primeiro carregue o teste de sensores abaixo, abra o Monitor Serial em 9600 baud e acione cada sensor, um por um.
- Depois carregue o sketch completo do alarme, troque a senha e teste o sistema só com o buzzer por alguns dias antes de ligar a sirene.
Primeiro teste: ver todos os sensores no Monitor Serial
Antes de passar para o código do alarme, confirme que cada sensor se comporta como você espera. Este pequeno sketch mostra duas vezes por segundo o estado do PIR, se a porta está aberta ou fechada, o número de pulsos de vibração no último meio segundo e o valor analógico do LDR. O pino de vibração é lido o tempo todo, porque os pulsos do SW-420 podem durar só alguns milissegundos, e um código que usa delay() no meio do caminho os perderia.
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}Máquina de estados: o cérebro do alarme
Se você tentar escrever um alarme com blocos if e delay(), ele logo vira uma bagunça: o teclado não é lido enquanto o retardo de entrada está esperando, e não dá para digitar a senha enquanto a sirene toca. A solução é uma máquina de estados, em que o sistema está em exatamente um estado a cada momento e passa de um estado a outro com eventos específicos. No código, os estados são definidos com um enum, o loop() aplica a cada ciclo só as regras do estado atual, e as esperas são medidas com millis() em vez de delay(). Assim o loop roda milhares de vezes por segundo e nenhuma tecla ou evento de sensor é perdido.
| Estado | Quando entra | O que faz | Quando sai |
|---|---|---|---|
| DESARMADO | Ao ligar e após senha + # | Monitora os sensores, mas nunca dispara; o LED verde acende quando tudo está pronto | Senha + A ou senha + B |
| RETARDO DE SAÍDA | Senha + A (ausente) ou B (em casa) | Conta 30 s regressivamente; o buzzer bipa uma vez por segundo e acelera nos últimos 10 s | ARMADO quando o tempo acaba; DESARMADO com senha + # |
| ARMADO | Quando o retardo de saída termina | Monitora todas as zonas; o LED vermelho fica aceso | ENTRADA se a porta abrir; ALARME em uma zona instantânea ou interna |
| RETARDO DE ENTRADA | Porta aberta com o sistema armado | O buzzer bipa por 20 s; PIR e laser ficam em espera | DESARMADO com senha + #; ALARME quando o tempo acaba |
| ALARME | Uma zona é acionada | Sirene e buzzer tocam; a sirene para após 3 min, o LED vermelho pisca rápido | DESARMADO só com senha + # |
Armar, desarmar e a senha no teclado
| Teclas | Função |
|---|---|
| 2580 + A | Modo ausente: as quatro zonas ativas, retardo de saída de 30 s |
| 2580 + B | Modo em casa (noturno): PIR desligado; porta, janela e laser ativos |
| 2580 + # | Desarmar; se houve alarme, mostra a zona acionada no Monitor Serial |
| * ou C | Apagar uma senha digitada errada |
| D | Teste de zonas: mostra os valores da porta, do PIR e do laser no Monitor Serial |
| 3 senhas erradas | O teclado fica bloqueado por 60 s; nesse tempo, até a senha certa é recusada |
Pedir a senha também para armar evita que crianças ou visitas armem o sistema sem querer. O código também faz uma verificação de não pronto: se você tentar armar com a porta aberta ou o feixe do laser interrompido, ouve um bipe longo e o sistema não arma, exatamente como nas centrais comerciais. Não deixe a senha no padrão 2580 e evite senhas fáceis de adivinhar, como 1234, 0000 ou o seu ano de nascimento. Se quiser trocar a senha sem carregar o sketch de novo, guarde-a na EEPROM e adicione um menu que permita digitar uma nova senha pelo teclado. Coloque um display LCD I2C ao lado do teclado e você verá também a contagem regressiva e as mensagens de estado na tela; é por isso que os pinos A4 e A5 ficaram livres.
Código do sistema de alarme com Arduino
O código abaixo é o coração deste guia. Na seção de configurações, no topo, você pode mudar a senha, os retardos, o tempo da sirene, o limite do laser e o tipo de relé; não precisa mexer no resto. Em um Arduino Uno ele usa cerca de 6 KB de memória de programa e 620 bytes de RAM, então sobra bastante espaço para um LCD ou um leitor 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}A espinha dorsal do código é a função suz(). Sempre que o valor bruto de uma entrada muda, é registrada uma marcação de tempo, e o estado estável só muda depois que o valor permanece igual por um tempo definido (50 ms para a porta, 200 ms para o PIR, 30 ms para o laser). Esse filtro simples impede que ruídos curtos em cabos longos e o repique dos contatos disparem o alarme. Ler o pino da porta com INPUT_PULLUP também é uma escolha proposital: com a porta fechada, o reed liga o pino ao GND e a leitura é LOW; quando a porta abre ou alguém corta o cabo, o pino vai para HIGH. Essa lógica de circuito fechado, usada em sistemas de alarme reais, trata um fio cortado exatamente como uma porta aberta.
A vibração é tratada de outro jeito. Mesmo uma única batida faz o SW-420 repicar várias vezes em poucos milissegundos; a titresimVar() primeiro conta os repiques dentro de 150 ms como um único evento e depois guarda os horários dos últimos três eventos. Se três eventos couberem em 3 segundos, isso conta como uma tentativa real de forçar a janela; o vento sacudindo a porta ou uma única bola batendo no vidro não disparam o alarme. A sirene para sozinha depois de 3 minutos, mas o sistema continua no estado ALARME e o LED vermelho pisca rápido para avisar a quem chegar em casa que houve um alarme. Quando você digita a senha certa, o nome da zona guardado na variável tetikleyen aparece no Monitor Serial; é uma versão pequena da memória de alarme das centrais comerciais.
Como ajustar os retardos de entrada e saída
Se os retardos forem curtos demais, você mesmo vai disparar o alarme; se forem longos demais, você dá tempo ao ladrão. Faça o retardo de saída igual ao dobro do tempo que você leva para ir do teclado até a porta e trancá-la, e o retardo de entrada mais ou menos o dobro do tempo para abrir a porta, ir até o teclado e digitar a senha. As centrais comerciais costumam usar 30–60 segundos para a saída e 15–30 segundos para a entrada. Instalar o teclado bem ao lado da porta, mas fora da vista de quem está do lado de fora, permite manter o retardo de entrada curto. O comportamento do buzzer também importa: na saída, um bipe curto a cada segundo acelera nos últimos 10 segundos para você sentir que o tempo está acabando; na entrada, um bipe intermitente de 200 ms lembra que você precisa digitar a senha.
Ligação da sirene e do relé
No circuito da sirene, o Arduino só aciona a bobina do relé; a corrente da sirene de 12 V passa pelos contatos do relé. A maioria dos módulos relé é ativa em LOW, ou seja, aciona quando o pino IN está em LOW; por isso o código tem a constante ROLE_AKTIF_LOW. O código escreve o nível de desligado antes de configurar o pino como saída, para que o relé não feche por um instante e faça a sirene dar um bipe enquanto o Arduino inicializa. Ao escolher uma sirene, confira a corrente: sirenes pequenas de 110 dB e 12 V costumam consumir 0,1–0,3 A, uma carga minúscula para um contato de relé de 10 A. Uma sirene externa com estrobo intimida com som e luz e mostra aos vizinhos de onde vem o alarme. As centrais profissionais costumam silenciar a sirene após 3 minutos, e alguns países chegam a ter um limite legal para isso, então mantenha SIREN_SURE em um valor razoável. Se preferir uma chave de estado sólido em vez de relé, você pode usar um MOSFET de nível lógico e, em sirenes com bobina (eletromagnéticas), colocar um diodo em paralelo com os terminais, em sentido inverso.
Avisos pelo Telegram com o ESP32
Uma sirene que ninguém escuta não serve para nada; o jeito mais fácil de saber que o alarme disparou enquanto você está fora é usar um ESP32 e o Telegram. O ESP32 se conecta ao seu Wi-Fi e envia mensagens ao servidor do Telegram por uma conexão criptografada, e você também pode mandar comandos para ele. Explicamos passo a passo como criar um bot com o BotFather, obter o token e o seu chat ID e instalar as bibliotecas UniversalTelegramBot e ArduinoJson no artigo sobre o bot do Telegram com ESP32. Esta versão não tem teclado; você controla o sistema pelo celular com os comandos /kur (armar ausente), /evde (modo em casa), /coz (desarmar) e /durum (estado). O bot só responde ao seu chat ID e ignora em silêncio os comandos de qualquer outra pessoa.
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}O código do ESP32 tem três diferenças importantes. Primeiro, enviar uma mensagem ao Telegram ou consultar novos comandos pode levar 1–2 segundos, e o loop() espera durante esse tempo. Uma porta que continua aberta e um PIR que fica em HIGH por pelo menos alguns segundos não são afetados, mas os pulsos de milissegundos do SW-420 poderiam ser perdidos; por isso a vibração é capturada com uma interrupção (attachInterrupt). Segundo, a alarmVer() liga a sirene primeiro e envia a mensagem depois, então, por mais lenta que esteja a internet, a sirene nunca atrasa. As notificações que falham entram em uma fila e são reenviadas a cada 15 segundos. Terceiro, a biblioteca Preferences grava na memória flash se o sistema está armado: se a energia cair e voltar com o sistema armado, o ESP32 inicia armado e avisa você de que a energia voltou. Se o relé e a sirene dividirem a mesma fonte fraca do ESP32, a placa pode reiniciar com o erro Brownout detector was triggered; você encontra a solução no artigo ESP32 reiniciando sem parar.
Alarme com Raspberry Pi Pico e MicroPython
Você pode montar a mesma máquina de estados em um Raspberry Pi Pico com um programa curto em MicroPython. O teclado é lido com a função de varredura de linhas e colunas do guia do teclado matricial; as linhas ficam em GP2–GP5 e as colunas em GP6–GP9, com pull-ups internos. Alimente o PIR pelo VBUS (5 V); a saída dele já é de 3,3 V, então ela vai direto no GP16. Alimente o SW-420 sempre pelo pino 3V3(OUT), porque a saída de um módulo alimentado com 5 V pode danificar a entrada do Pico. Os pulsos de vibração são capturados com Pin.irq e seus horários ficam guardados em uma lista, enquanto os estados são controlados como textos simples. O tempo é medido com time.ticks_diff(), porque essa função dá a diferença certa mesmo quando o temporizador volta ao início.
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)Como reduzir os alarmes falsos
O pior inimigo de um sistema de alarme não é o ladrão, e sim o alarme falso: depois que a sirene tocar à toa três vezes, nem você nem os seus vizinhos vão levá-la a sério. Algumas das medidas abaixo já estão no código; as outras dizem respeito à instalação e à fiação:
- Espere o aquecimento: O PIR pode disparar sozinho nos primeiros 30–60 segundos depois de ligado; o código ignora os primeiros 60 segundos.
- Use um filtro: Esperar que cada entrada fique estável por um tempo definido (debounce) elimina ruídos curtos nos cabos.
- Conte as batidas: Procurar 3 batidas em 3 segundos, em vez de uma única, filtra o vento, os caminhões que passam e as portas batendo. Se precisar, aumente a contagem para 4–5 ou reduza a sensibilidade no trimpot da placa.
- Posicione bem o PIR: Não aponte para aquecedores, ar-condicionado, janelas com sol ou cortinas; ele detecta melhor quando o movimento cruza o campo de visão de um lado para o outro. Se você tem animais de estimação, instale o sensor no alto, incline-o um pouco para baixo e reduza a sensibilidade.
- Exija dois sensores: Em áreas críticas, exigir que o PIR e o laser, ou o PIR e a porta, disparem com poucos segundos de diferença (zona cruzada) impede que um único sensor com defeito cause um alarme.
- Limpe a alimentação: A queda de tensão quando o relé aciona pode disparar o PIR. Coloque um capacitor de 100 µF e um de 100 nF entre o VCC e o GND de cada sensor, e passe os fios do relé e da sirene separados dos fios dos sensores.
- Use pull-ups externos em cabos longos: O pull-up interno do Arduino é de 20–50 kΩ e capta ruído em um cabo de 10 metros; um resistor externo de 4,7 kΩ mais um capacitor de 100 nF no pino deixam a linha bem mais robusta.
- Proteja o laser da luz ambiente: Coloque o LDR dentro de um tubo preto e teste o limite separadamente com luz do dia e à noite.
Erros comuns e soluções
- O alarme dispara assim que é armado: O PIR ainda está aquecendo ou o contato da porta foi ligado errado. No teste de sensores, a porta deve ler LOW quando fechada; se ler HIGH, o ímã está longe demais ou há um fio partido.
- O teclado não responde ou lê a tecla errada: Os fios das linhas e das colunas estão trocados. Em vez de refazer a fiação, mude a ordem dos arrays satirPinleri e sutunPinleri no código.
- Você ouve um bipe longo ao tentar armar: Não é defeito, é o aviso de não pronto: a porta está aberta ou o feixe do laser não está chegando ao LDR. Aperte D para verificar as zonas.
- O relé dá um clique ao ligar: O nível de desligado não foi escrito antes de o pino virar saída, ou ROLE_AKTIF_LOW ficou como true em um relé ativo em HIGH.
- O Arduino reinicia quando a sirene toca: A sirene e o Arduino dividem uma fonte fraca, e a queda de tensão reinicia a placa. Use uma fonte mais forte, um conversor separado e fios mais grossos; detalhes em Arduino reiniciando sozinho.
- O sensor de vibração nunca dispara ou dispara o tempo todo: Gire devagar o trimpot da placa para ajustar o limiar e cole o sensor firmemente no vidro ou no batente; um sensor solto não transmite a batida.
- O ESP32 não envia mensagens: O token ou o chat ID está errado, a rede é de 5 GHz ou o bot nunca foi iniciado no Telegram. O bot não consegue mandar mensagens para você até que você envie /start para ele uma vez.
- O laser funciona bem de dia, mas dá alarmes falsos à noite (ou o contrário): O limite está perto demais do nível de luz ambiente. Meça os valores com e sem feixe nas duas condições e escolha um novo LAZER_ESIK no meio.
Ideias para melhorar seu sistema de alarme
- Armar e desarmar com cartão: Adicione um leitor RFID RC522 ao lado do teclado e desarme com um chaveiro.
- Zonas 24 horas: Adicione um sensor de gás MQ-2 ou um sensor de chama para criar uma zona de gás e incêndio que dispara mesmo com o sistema desarmado.
- Sensores de porta sem fio: Faça contatos de porta a bateria para janelas distantes com módulos RF de 433 MHz.
- Painel com display: Mostre a contagem regressiva, o estado e a memória de alarme em um LCD I2C.
- Integração com casa inteligente: Conecte o ESP32 ao MQTT e Home Assistant e controle o alarme junto com as luzes e câmeras.
- Contato antiviolação (tamper): Coloque uma microchave na tampa da caixa para o buzzer tocar quando a caixa for aberta, mesmo com o sistema desarmado.
- Projeto de maquete: Reúna todas essas zonas em uma maquete como a camada de segurança de um projeto de casa inteligente.
Como junta leitura de sensores, temporização, máquina de estados e projeto de alimentação em um só projeto, um sistema de alarme com Arduino também é uma ótima forma de aprender. Rode primeiro o teste de sensores, depois teste o alarme só com o buzzer por alguns dias e ligue a sirene por último. No app BlueGrays você encontra projetos como Sensor de porta com chave reed, Sistema de segurança a laser, Fechadura com senha usando teclado 4x4 e LCD e Sistema de alarme de movimento com ESP32 e avisos pelo Telegram, cada um com esquemas do circuito e códigos passo a passo.
Perguntas frequentes
Como fazer um sistema de alarme com Arduino?
Você liga a um Arduino Uno um sensor de movimento PIR, um contato reed para a porta, um teclado matricial, um buzzer ativo e um módulo relé que aciona uma sirene. O código é escrito como uma máquina de estados que leva o sistema pelos estados desarmado, retardo de saída, armado, retardo de entrada e alarme. Você arma e desarma com uma senha no teclado e, se um sensor disparar com o sistema armado, a sirene toca.
Quais sensores são usados em um sistema de alarme com Arduino?
Os mais comuns são o sensor de movimento PIR (HC-SR501) para ambientes internos, contatos reed (MC-38) para portas e janelas, o sensor de vibração SW-420 para batidas no vidro e uma barreira a laser para corredores. Você pode adicionar um MQ-2 para gás e um sensor de chama para incêndio. Cada sensor é definido como uma zona separada, com comportamento próprio.
O alarme com Arduino funciona quando falta energia?
Não, se for alimentado só por uma fonte de tomada. Com uma fonte de alarme de 13,8 V que carrega uma bateria de 12 V 7 Ah, um sistema com Uno consumindo cerca de 100 mA funciona por 4–5 dias. Na versão com ESP32, o estado armado fica gravado na flash, então o sistema volta armado quando a energia retorna; o roteador também precisa de um nobreak para os avisos saírem.
O alarme com Arduino pode enviar notificações para o meu celular?
Um Arduino Uno sozinho não consegue, mas um ESP32 pode enviar mensagens instantâneas pelo Telegram via Wi-Fi e receber do seu celular comandos como /kur e /coz. Onde não há internet, um módulo GSM SIM800L pode enviar SMS ou fazer uma ligação. Usar os dois métodos juntos é a opção mais confiável.
Por que meu sensor PIR dá alarmes falsos?
As causas mais comuns são o aquecimento de 30–60 segundos depois de ligar, fontes de calor como aquecedores e ar-condicionado, cortinas balançando sob o sol quente e ruído na alimentação quando um relé aciona. Ignorar o aquecimento no código, manter o sensor longe de fontes de calor e colocar um capacitor entre VCC e GND resolvem a maioria desses problemas.
Um sistema de alarme com Arduino pode ser usado em uma casa de verdade?
Ele funciona bem para aprender e para monitorar um quarto, uma garagem ou um depósito, mas não substitui um sistema de alarme certificado. Os sistemas comerciais são testados segundo normas como a EN 50131 quanto a proteção antiviolação, alimentação de reserva, comunicação sem fio criptografada e conexão com uma central de monitoramento. Se for usar um sistema com Arduino em casa, recomendamos adicionar uma bateria de reserva, um contato antiviolação e um segundo canal de notificação.
Projetos para montar depois desta leitura
Ver todos os projetos
Alarme de movimento com ESP32 (PIR + buzzer + LED + Telegram)
Um sistema de alarme de segurança inteligente que aciona o buzzer e o LED quando o sensor PIR detecta movimento e, ao mesmo tempo, envia uma notificação instantânea ao proprietário pelo Telegram. Inclui um intervalo de espera de 30 segundos para limitar a frequência dos alarmes repetidos; esse mecanismo não valida as detecções do PIR nem elimina disparos falsos. Pode ser usado na segurança de casas, no monitoramento de garagens, depósitos ou salas de trabalho.

Sistema de segurança com laser
Como nas cenas de roubo com lasers dos filmes: um feixe de laser atinge um sensor de luz e, assim que alguém passa e interrompe o feixe, o LED vermelho acende e o buzzer começa a soar. O alarme não pa

Sistema de alerta com laser
Você monta uma barreira de luz invisível, como nos filmes. Um laser ilumina um sensor de luz do outro lado; assim que alguém passa e interrompe o feixe, o LED acende e o buzzer soa. Quando o feixe vol

Sensor de porta com chave reed
Saiba se a porta está aberta ou fechada só de olhar uma luz. Você fixa um pequeno ímã na porta e uma chave sensível ao ímã no batente; quando a porta está fechada, os dois ficam lado a lado e o LED ve

Fechadura com senha, teclado 4x4 e LCD
Uma pequena fechadura eletrônica que impede a entrada de quem não sabe a senha. Você pressiona a cerquilha e digita a senha no teclado. Se estiver correta, o display indica que o acesso foi permitido,

Detector de movimento com sensor de movimento
Um vigia simples que avisa quando alguém entra no cômodo. O sensor de movimento detecta o calor emitido pelo corpo humano, e o Arduino mantém o LED aceso enquanto há movimento e o apaga quando ele par

Cofre eletrônico inteligente
Um pequeno cofre para guardar seus objetos de valor, que abre com uma senha. Você digita sua senha de quatro dígitos no teclado; se estiver correta, o servo motor abre a trava, a luz verde acende e o
Mais artigos
App BlueGrays
Monte este circuito passo a passo pelo celular
Esquema do circuito, código e lista de componentes em uma só tela. Mais de 200 projetos com Arduino, ESP32 e Pico prontos para montar.
Escaneie com seu celular
Aponte a câmera para o código para abrir a loja de aplicativos do seu celular.
ou abrir a loja

