0

Allarme Antifurto con PIR Multipli AI.

Share

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;
}