Introduzione al Sistema di Allarme
Un sistema di allarme antifurto efficace è fondamentale per garantire la sicurezza delle nostre abitazioni e dei nostri spazi commerciali. Con un programma adeguato, è possibile integrare diversi componenti, come PIR multipli, sirena e batterie tampone, per un sistema altamente funzionale.
Ecco uno sketch Arduino completo e modulare per un antifurto con più PIR (zone), ritardo ingresso/uscita, sirena, batteria tampone (monitor tensione), e notifica via GSM (SIM800L) oppure via ESP8266 (webhook/IFTTT). Include gestione stati, antitamper, log su Serial e parametrizzazione.
Schema e componentiMCU:
Arduino Uno/Nano o Mega.
Alimentazione principale 12 V con step-down a 5 V;
batteria tampone 12 V piombo-gel o LiFePO4 con modulo di ricarica esterno e monitoraggio tensione su ingresso analogico tramite partitore.
Sensori:
PIR NC/NO a 5 V;
collegali a ingressi digitali con pull-up interno e, se NC, logica invertita in codice.
Prevedi un ingresso TAMPER NC in serie al coperchio/sabotaggio.
Attuatori: Sirena 12 V con MOSFET o relè; LED di stato; buzzer opzionale per beeps.
Notifica:GSM SIM800L con alimentazione stabile 4 V e picchi 2 A, livello logico compatibile e libreria SIM800L per SMS/chiamata.
ESP8266 (NodeMCU/ESP-01) per HTTP POST verso Webhook/IFTTT o endpoint tuo; usa client HTTP.
Funzionalità implementateStati: DISARMED, EXIT_DELAY, ARMED, ENTRY_DELAY, ALARM.
Ritardi configurabili (ingresso/uscita), zone immediate e ritardate, esclusione zone.
Antitamper sempre attivo: scatta allarme h24 indipendentemente dallo stato.
Sirena con durata massima e autoestensione se movimento continua, non bloccante (millis).
Notifiche: SMS via SIM800L o HTTP webhook via ESP8266; selezionabili con define.
Monitor batteria: soglia bassa invia notifica “Battery low” e log.
Codice Arduino (sketch unico)Copia e incolla, poi personalizza le sezioni CONFIG. Per usare GSM metti USE_GSM true; per ESP8266 via seriale AT/bridge o client dedicato, metti USE_ESP8266 true e compila per ESP8266 se integri logica sul modulo.
/* Antifurto multi-PIR con ritardi, sirena, tamper, batteria, GSM/ESP8266
Note componenti: SIM800L richiede alimentazione 3.8-4.2V con picchi 2A; usare step-down dedicato e condensatori. [web:24]
IFTTT/Webhook: endpoint HTTP POST con chiave personale. [web:16]
*/
#include <SoftwareSerial.h>
#include <EEPROM.h>
// ========== CONFIGURAZIONE ==========
const bool USE_GSM = true; // true: usa SIM800L SMS; false per disattivare
const bool USE_ESP8266 = false; // true: invia HTTP POST a webhook via ESP8266
// Pin assegnazioni (adatta alla tua board)
const uint8_t SIREN_PIN = 8; // uscita relè/MOSFET sirena
const uint8_t LED_ARMED_PIN = 9; // LED stato armato
const uint8_t BUZZER_PIN = 6; // opzionale
const uint8_t TAMPER_PIN = 7; // ingresso tamper NC -> usa pullup
// PIR: elenco pin (immediate o delay zone)
const uint8_t PIR_PINS[] = {2, 3, 4, 5}; // fino a 4 PIR
const bool PIR_DELAYED[] = {false, true, false, true}; // true = zona ritardata
const bool PIR_ACTIVE_LOW = false; // se sensori NC con pullup -> true
// Ingresso chiave/tastiera per ARM/DISARM (qui un semplice interruttore)
const uint8_t ARM_SWITCH_PIN = 10; // HIGH = armato
const bool ARM_SWITCH_ACTIVE_HIGH = true;
// Batteria su analogico con partitore
const uint8_t VBAT_PIN = A0;
const float R1 = 100000.0; // ohm (alto verso batteria)
const float R2 = 33000.0; // ohm (basso verso GND)
const float ADC_REF = 5.0; // V
const int ADC_RES = 1023;
const float VBAT_LOW_THRESHOLD = 11.6; // V batteria bassa 12V piombo-gel
// Tempi (ms)
unsigned long EXIT_DELAY_MS = 30000; // ritardo uscita
unsigned long ENTRY_DELAY_MS = 20000; // ritardo ingresso (zone ritardate)
unsigned long SIREN_MAX_MS = 180000; // durata massima sirena
unsigned long SIREN_EXT_MS = 20000; // estensione dopo ultimo movimento [web:14]
// Notifica
// GSM: numero di destinazione
const char PHONE_NUMBER[] = "+39XXXXXXXXXX"; // Inserisci il tuo
// ESP8266/Webhook (IFTTT esempio)
const char WEBHOOK_HOST[] = "maker.ifttt.com";
const int WEBHOOK_PORT = 80;
const char WEBHOOK_PATH[] = "/trigger/alarm_event/with/key/YOUR_IFTTT_KEY"; // [web:16]
// Seriali
// SIM800L su D11 (RX arduino) e D12 (TX arduino) come esempio
SoftwareSerial gsmSS(11, 12); // RX, TX
// ESP8266 su D13 (RX) e D14(A0 su UNO non va; su Mega usare Serial1). Adatta ai tuoi pin/board.
//SoftwareSerial espSS(13, 14);
// ========== STATI ==========
enum State { DISARMED, EXIT_DELAY, ARMED, ENTRY_DELAY, ALARM };
State state = DISARMED;
// ========== VARIABILI ==========
unsigned long stateStart = 0;
unsigned long lastMotionTime = 0;
bool sirenOn = false;
unsigned long sirenStart = 0;
bool notifiedAlarm = false;
bool notifiedBattery = false;
// ========== UTILITY ==========
float readBatteryVoltage() {
int raw = analogRead(VBAT_PIN);
float vout = (raw * ADC_REF) / ADC_RES;
float vin = vout * (R1 + R2) / R2;
return vin;
}
bool readDigitalActive(uint8_t pin, bool activeLow) {
int v = digitalRead(pin);
return activeLow ? (v == LOW) : (v == HIGH);
}
void beep(uint8_t times, uint16_t onMs=80, uint16_t offMs=100) {
for (uint8_t i=0;i<times;i++){
tone(BUZZER_PIN, 2500);
delay(onMs);
noTone(BUZZER_PIN);
delay(offMs);
}
}
void setSiren(bool on) {
if (on && !sirenOn) {
digitalWrite(SIREN_PIN, HIGH);
sirenOn = true;
sirenStart = millis();
} else if (!on && sirenOn) {
digitalWrite(SIREN_PIN, LOW);
sirenOn = false;
}
}
// ========== NOTIFICHE ==========
void gsmSendSMS(const char* msg) {
if (!USE_GSM) return;
gsmSS.println("AT");
delay(200);
gsmSS.println("AT+CMGF=1");
delay(200);
gsmSS.print("AT+CMGS="");
gsmSS.print(PHONE_NUMBER);
gsmSS.println(""");
delay(200);
gsmSS.print(msg);
gsmSS.write(26); // CTRL+Z
delay(500);
}
// Esempio minimale via ESP8266 con comandi AT HTTP (alternativa: client nativo su ESP8266)
void espSendWebhook(const char* eventJson) {
if (!USE_ESP8266) return;
// Qui si assume firmware AT e connessione WiFi già configurata; per produzione usa client HTTP su firmware ESP. [web:16][web:22]
// Invio POST semplice
// AT+CIPSTART="TCP","maker.ifttt.com",80
// AT+CIPSEND=length
// POST ...
Content-Length: ...
{json}
}
// Helper per inviare notifica unica per evento
void notifyEvent(const char* text, const char* jsonKey="event") {
if (USE_GSM) {
gsmSendSMS(text); // SMS semplice. [web:18][web:24]
}
if (USE_ESP8266) {
// Costruisci JSON minimale
// {"value1":"text"}
// IFTTT usa value1/value2/value3. [web:16]
// Implementazione client su ESP consigliata.
}
Serial.print("[NOTIFY] "); Serial.println(text);
}
// ========== SETUP ==========
void setup() {
pinMode(SIREN_PIN, OUTPUT);
digitalWrite(SIREN_PIN, LOW);
pinMode(LED_ARMED_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(TAMPER_PIN, INPUT_PULLUP);
for (uint8_t i=0;i<sizeof(PIR_PINS);i++) {
pinMode(PIR_PINS[i], PIR_ACTIVE_LOW ? INPUT_PULLUP : INPUT);
}
pinMode(ARM_SWITCH_PIN, ARM_SWITCH_ACTIVE_HIGH ? INPUT_PULLUP : INPUT_PULLUP);
Serial.begin(9600);
if (USE_GSM) {
gsmSS.begin(4800); // baud comune per SIM800L; regola se necessario. [web:18]
delay(300);
}
// if (USE_ESP8266) espSS.begin(115200); // dipende firmware
state = DISARMED;
stateStart = millis();
digitalWrite(LED_ARMED_PIN, LOW);
beep(2);
Serial.println("Antifurto pronto.");
}
// ========== LOGICA ==========
bool anyImmediateZoneTriggered() {
for (uint8_t i=0;i<sizeof(PIR_PINS);i++) {
if (!PIR_DELAYED[i]) {
if (readDigitalActive(PIR_PINS[i], PIR_ACTIVE_LOW)) return true;
}
}
return false;
}
bool anyDelayedZoneTriggered() {
for (uint8_t i=0;i<sizeof(PIR_PINS);i++) {
if (PIR_DELAYED[i]) {
if (readDigitalActive(PIR_PINS[i], PIR_ACTIVE_LOW)) return true;
}
}
return false;
}
bool tamperTriggered() {
// Tamper NC su pullup: LOW = chiuso, HIGH = apertura tamper
return digitalRead(TAMPER_PIN) == HIGH;
}
bool armSwitchOn() {
bool v = digitalRead(ARM_SWITCH_PIN) == (ARM_SWITCH_ACTIVE_HIGH ? HIGH : LOW);
return v;
}
void loop() {
unsigned long now = millis();
// Monitor batteria periodico
static unsigned long lastBat = 0;
if (now - lastBat > 10000) {
lastBat = now;
float vb = readBatteryVoltage();
if (vb > 0 && vb < VBAT_LOW_THRESHOLD && !notifiedBattery) {
notifyEvent("Allarme: Batteria bassa");
notifiedBattery = true;
}
}
// Antitamper 24h
if (tamperTriggered()) {
if (state != ALARM) {
state = ALARM;
stateStart = now;
lastMotionTime = now;
notifiedAlarm = false;
Serial.println("TAMPER -> ALARM");
}
}
// Gestione comando ARM/DISARM
static bool lastArm = false;
bool armReq = armSwitchOn();
if (state == DISARMED) {
digitalWrite(LED_ARMED_PIN, LOW);
if (armReq && !lastArm) {
state = EXIT_DELAY;
stateStart = now;
beep(1,150,0);
Serial.println("Arm richiesto -> EXIT_DELAY");
}
} else if (state == EXIT_DELAY) {
digitalWrite(LED_ARMED_PIN, (now/300)%2); // lampeggio
if (!armReq && lastArm) {
state = DISARMED;
stateStart = now;
beep(2);
Serial.println("Arm annullato -> DISARMED");
} else if (anyImmediateZoneTriggered() || anyDelayedZoneTriggered()) {
// Durante uscita ignora movimenti; opzionale: beep su movimento
}
if (now - stateStart >= EXIT_DELAY_MS) {
state = ARMED;
stateStart = now;
digitalWrite(LED_ARMED_PIN, HIGH);
beep(3,60,60);
Serial.println("Sistema ARMATO");
notifiedBattery = false; // reset ciclico
}
} else if (state == ARMED) {
digitalWrite(LED_ARMED_PIN, HIGH);
if (!armReq && lastArm) {
state = DISARMED;
stateStart = now;
setSiren(false);
Serial.println("Disarm -> DISARMED");
beep(2);
} else {
if (anyImmediateZoneTriggered()) {
state = ALARM;
stateStart = now;
lastMotionTime = now;
notifiedAlarm = false;
Serial.println("Zona immediata -> ALARM");
} else if (anyDelayedZoneTriggered()) {
state = ENTRY_DELAY;
stateStart = now;
beep(1,60,0);
Serial.println("Zona ritardata -> ENTRY_DELAY");
}
}
} else if (state == ENTRY_DELAY) {
digitalWrite(LED_ARMED_PIN, (now/200)%2);
if (!armReq && lastArm) {
// Disarmo in ingresso
state = DISARMED;
stateStart = now;
beep(2);
Serial.println("Disarm durante ENTRY_DELAY -> DISARMED");
} else {
// Se scattano zone immediate durante entry -> ALARM
if (anyImmediateZoneTriggered()) {
state = ALARM;
stateStart = now;
lastMotionTime = now;
notifiedAlarm = false;
Serial.println("Immediata durante ENTRY -> ALARM");
}
if (now - stateStart >= ENTRY_DELAY_MS) {
state = ALARM;
stateStart = now;
lastMotionTime = now;
notifiedAlarm = false;
Serial.println("ENTRY scaduto -> ALARM");
}
}
} else if (state == ALARM) {
setSiren(true);
// Estensione sirena se movimento continua
if (anyImmediateZoneTriggered() || anyDelayedZoneTriggered()) {
lastMotionTime = now;
}
// Notifica una volta per evento
if (!notifiedAlarm) {
notifyEvent("Allarme IN CORSO!");
notifiedAlarm = true;
}
// Spegnimento per timeout + coda estesa
bool sirenTimeout = (now - sirenStart >= SIREN_MAX_MS);
bool extensionActive = (now - lastMotionTime <= SIREN_EXT_MS);
if (sirenTimeout && !extensionActive) {
setSiren(false);
}
// Disarmo spegne e torna DISARMED
if (!armReq && lastArm) {
setSiren(false);
state = DISARMED;
stateStart = now;
beep(2);
Serial.println("Disarm -> DISARMED");
}
}
lastArm = armReq;
}
