// ============================================================
// SISTEMA DI ALLARME UNIFICATO - Singola Board Arduino Uno
// ============================================================
// Componenti:
// - Display LCD I2C (0x27)
// - Tastierino 4x4
// - 2 Sensori PIR
// - Buzzer piezoelettrico passivo
// - LED verde (allarme disattivo)
// - LED rosso (allarme attivo)
//
// Funzionalita password:
// - Al primo avvio: richiede impostazione password (salvata in EEPROM)
// - La password sopravvive allo spegnimento della scheda
// - Dal menu si puo' cambiare la password
// - Ogni inserimento di 6 cifre richiede conferma con #
//
// Approccio: Macchina a stati NON-BLOCCANTE
// ============================================================
#include <Wire.h>
#include <Keypad.h>
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>
// -------------------- PIN --------------------
const byte rowPins[4] = {12, 11, 10, 9};
const byte colPins[4] = {8, 7, 6, 5};
const int pinLedVerde = 2;
const int pinLedRosso = 4;
const int pinBuzzer = 3;
const int pinSensore1 = A0;
const int pinSensore2 = A1;
// -------------------- TASTIERINO --------------------
char sediciTasti[4][4] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
Keypad tastierino = Keypad(makeKeymap(sediciTasti), rowPins, colPins, 4, 4);
// -------------------- LCD --------------------
LiquidCrystal_I2C lcd(0x27, 16, 2);
// -------------------- PASSWORD --------------------
#define PWD_LEN 7
char master[PWD_LEN];
char userInput[PWD_LEN];
char nuovaPwd[PWD_LEN];
byte pressCount = 0;
// -------------------- EEPROM --------------------
const int EEPROM_FLAG_ADDR = 0;
const int EEPROM_PWD_ADDR = 1;
const byte EEPROM_FLAG_SET = 0xA5;
// -------------------- STATI --------------------
enum Stato {
SPLASH,
FIRST_BOOT_MSG,
IMPOSTA_PWD,
IMPOSTA_PWD_INVIO,
IMPOSTA_PWD_CONF,
IMPOSTA_PWD_CONF_INVIO,
MSG_PWD_SALVATA,
MSG_PWD_NO_MATCH,
ATTESA,
LOGIN,
VERIFICA,
MSG_NEGATO,
MSG_PERMESSO,
MENU,
CONFERMA_ON,
CONFERMA_OFF,
MSG_CONFERMATO,
MSG_NON_VALIDO,
CAMBIO_PWD_NEW,
CAMBIO_PWD_NEW_INVIO,
CAMBIO_PWD_CONF,
CAMBIO_PWD_CONF_INVIO,
SIRENA,
SIRENA_PWD,
SIRENA_CONFERMA,
SIRENA_PWD_ERRATA
};
Stato statoCorrente = SPLASH;
Stato prossimoStato = ATTESA;
bool cambioPasswordMode = false;
// -------------------- VARIABILI DI STATO --------------------
bool allarmeAttivo = false;
bool schermataAggiornata = false;
unsigned long tempoStatoInizio = 0;
// -------------------- SIRENA NON-BLOCCANTE --------------------
int sirenaFreq = 2000;
int sirenaDirezione = 1;
unsigned long sirenaUltimoStep = 0;
const unsigned long SIRENA_STEP_MS = 1;
const int SIRENA_FREQ_MIN = 2000;
const int SIRENA_FREQ_MAX = 2800;
// ==============================================================
// FUNZIONI EEPROM
// ==============================================================
bool passwordPresente() {
return (EEPROM.read(EEPROM_FLAG_ADDR) == EEPROM_FLAG_SET);
}
void caricaPassword() {
for (int i = 0; i < 6; i++) {
master[i] = (char)EEPROM.read(EEPROM_PWD_ADDR + i);
}
master[6] = '\0';
}
void salvaPassword(const char* pwd) {
for (int i = 0; i < 6; i++) {
EEPROM.update(EEPROM_PWD_ADDR + i, pwd[i]);
}
EEPROM.update(EEPROM_FLAG_ADDR, EEPROM_FLAG_SET);
memcpy(master, pwd, 6);
master[6] = '\0';
}
// ==============================================================
// SETUP
// ==============================================================
void setup() {
Serial.begin(9600);
lcd.init();
lcd.backlight();
pinMode(pinLedVerde, OUTPUT);
pinMode(pinLedRosso, OUTPUT);
digitalWrite(pinLedVerde, HIGH);
digitalWrite(pinLedRosso, LOW);
pinMode(pinBuzzer, OUTPUT);
pinMode(pinSensore1, INPUT);
pinMode(pinSensore2, INPUT);
if (passwordPresente()) {
caricaPassword();
}
cambiaStato(SPLASH);
}
// ==============================================================
// LOOP PRINCIPALE
// ==============================================================
void loop() {
char tasto = tastierino.getKey();
switch (statoCorrente) {
// ========== SPLASH ==========
case SPLASH:
if (!schermataAggiornata) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("** Sistema **");
lcd.setCursor(0, 1);
lcd.print("** di allarme **");
schermataAggiornata = true;
}
if (millis() - tempoStatoInizio >= 3000) {
if (!passwordPresente()) {
cambiaStato(FIRST_BOOT_MSG);
} else {
cambiaStato(ATTESA);
}
}
break;
// ========== FIRST_BOOT_MSG ==========
case FIRST_BOOT_MSG:
if (!schermataAggiornata) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Nessuna password");
lcd.setCursor(0, 1);
lcd.print("Premi # per imp.");
schermataAggiornata = true;
}
if (tasto == '#') {
cambioPasswordMode = false;
clearData();
cambiaStato(IMPOSTA_PWD);
}
break;
// ========== IMPOSTA_PWD: digita nuova password ==========
case IMPOSTA_PWD:
if (!schermataAggiornata) {
clearNuovaPwd();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Nuova password:");
lcd.setCursor(0, 1);
lcd.print("______");
schermataAggiornata = true;
}
if (tasto != NO_KEY) {
if (tasto == '*') {
clearData();
cambiaStato(IMPOSTA_PWD);
} else if (tasto != '#') {
nuovaPwd[pressCount] = tasto;
lcd.setCursor(pressCount, 1);
lcd.print("*");
pressCount++;
if (pressCount == 6) {
nuovaPwd[6] = '\0';
pressCount = 0;
cambiaStato(IMPOSTA_PWD_INVIO);
}
}
}
break;
// ========== IMPOSTA_PWD_INVIO: # per confermare ==========
case IMPOSTA_PWD_INVIO:
if (!schermataAggiornata) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" '#' per INVIO");
lcd.setCursor(0, 1);
lcd.print(" '*' per CANC.");
schermataAggiornata = true;
}
if (tasto != NO_KEY) {
if (tasto == '#') {
clearData();
cambiaStato(IMPOSTA_PWD_CONF);
} else if (tasto == '*') {
clearData();
cambiaStato(IMPOSTA_PWD);
} else {
prossimoStato = IMPOSTA_PWD_INVIO;
cambiaStato(MSG_NON_VALIDO);
}
}
break;
// ========== IMPOSTA_PWD_CONF: digita conferma password ==========
case IMPOSTA_PWD_CONF:
if (!schermataAggiornata) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Conferma pwd:");
lcd.setCursor(0, 1);
lcd.print("______");
schermataAggiornata = true;
}
if (tasto != NO_KEY) {
if (tasto == '*') {
clearData();
cambiaStato(IMPOSTA_PWD);
} else if (tasto != '#') {
userInput[pressCount] = tasto;
lcd.setCursor(pressCount, 1);
lcd.print("*");
pressCount++;
if (pressCount == 6) {
userInput[6] = '\0';
pressCount = 0;
cambiaStato(IMPOSTA_PWD_CONF_INVIO);
}
}
}
break;
// ========== IMPOSTA_PWD_CONF_INVIO: # per confermare ==========
case IMPOSTA_PWD_CONF_INVIO:
if (!schermataAggiornata) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" '#' per INVIO");
lcd.setCursor(0, 1);
lcd.print(" '*' per CANC.");
schermataAggiornata = true;
}
if (tasto != NO_KEY) {
if (tasto == '#') {
if (strcmp(nuovaPwd, userInput) == 0) {
salvaPassword(nuovaPwd);
clearData();
cambiaStato(MSG_PWD_SALVATA);
} else {
clearData();
cambiaStato(MSG_PWD_NO_MATCH);
}
} else if (tasto == '*') {
clearData();
cambiaStato(IMPOSTA_PWD);
} else {
prossimoStato = IMPOSTA_PWD_CONF_INVIO;
cambiaStato(MSG_NON_VALIDO);
}
}
break;
// ========== MSG_PWD_SALVATA ==========
case MSG_PWD_SALVATA:
if (!schermataAggiornata) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" Password ");
lcd.setCursor(0, 1);
lcd.print(" salvata! ");
schermataAggiornata = true;
}
if (millis() - tempoStatoInizio >= 2000) {
clearData();
if (cambioPasswordMode) {
cambiaStato(ATTESA);
} else {
cambiaStato(ATTESA);
}
}
break;
// ========== MSG_PWD_NO_MATCH ==========
case MSG_PWD_NO_MATCH:
if (!schermataAggiornata) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Le password non ");
lcd.setCursor(0, 1);
lcd.print("corrispondono! ");
schermataAggiornata = true;
}
if (millis() - tempoStatoInizio >= 2000) {
clearData();
cambiaStato(IMPOSTA_PWD);
}
break;
// ========== ATTESA ==========
case ATTESA:
if (!schermataAggiornata) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(allarmeAttivo ? " Allarme: ON " : " Allarme: OFF");
lcd.setCursor(0, 1);
lcd.print(" Premi un tasto");
schermataAggiornata = true;
}
if (allarmeAttivo && (digitalRead(pinSensore1) == HIGH || digitalRead(pinSensore2) == HIGH)) {
cambiaStato(SIRENA);
break;
}
if (tasto != NO_KEY) {
clearData();
cambiaStato(LOGIN);
}
break;
// ========== LOGIN ==========
case LOGIN:
if (!schermataAggiornata) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Accesso Sistema");
lcd.setCursor(0, 1);
lcd.print("Password:");
schermataAggiornata = true;
}
if (tasto != NO_KEY) {
if (tasto == '*') {
clearData();
cambiaStato(LOGIN);
} else if (tasto == '#') {
prossimoStato = ATTESA;
cambiaStato(MSG_NEGATO);
} else {
userInput[pressCount] = tasto;
lcd.setCursor(pressCount + 9, 1);
lcd.print("*");
pressCount++;
if (pressCount == 6) {
userInput[6] = '\0';
cambiaStato(VERIFICA);
}
}
}
break;
// ========== VERIFICA ==========
case VERIFICA:
if (!schermataAggiornata) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" '#' per INVIO");
lcd.setCursor(0, 1);
lcd.print(" '*' per CANC.");
schermataAggiornata = true;
}
if (tasto != NO_KEY) {
if (tasto == '#') {
if (strcmp(userInput, master) == 0) {
prossimoStato = MENU;
cambiaStato(MSG_PERMESSO);
} else {
prossimoStato = ATTESA;
cambiaStato(MSG_NEGATO);
}
} else if (tasto == '*') {
clearData();
cambiaStato(LOGIN);
} else {
prossimoStato = VERIFICA;
cambiaStato(MSG_NON_VALIDO);
}
}
break;
// ========== MSG_PERMESSO ==========
case MSG_PERMESSO:
if (!schermataAggiornata) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("ACCESSO PERMESSO");
schermataAggiornata = true;
}
if (millis() - tempoStatoInizio >= 2000) {
clearData();
cambiaStato(prossimoStato);
}
break;
// ========== MSG_NEGATO ==========
case MSG_NEGATO:
if (!schermataAggiornata) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" ACCESSO NEGATO ");
schermataAggiornata = true;
}
if (millis() - tempoStatoInizio >= 2000) {
clearData();
cambiaStato(prossimoStato);
}
break;
// ========== MENU ==========
case MENU:
if (!schermataAggiornata) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(allarmeAttivo ? " Allarme: ON " : " Allarme: OFF");
lcd.setCursor(0, 1);
lcd.print("1=ON 2=OFF 3=Pwd");
schermataAggiornata = true;
}
if (tasto != NO_KEY) {
if (tasto == '1') {
cambiaStato(CONFERMA_ON);
} else if (tasto == '2') {
cambiaStato(CONFERMA_OFF);
} else if (tasto == '3') {
clearData();
cambioPasswordMode = true;
cambiaStato(CAMBIO_PWD_NEW);
} else {
prossimoStato = MENU;
cambiaStato(MSG_NON_VALIDO);
}
}
break;
// ========== CONFERMA_ON ==========
case CONFERMA_ON:
if (!schermataAggiornata) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" Allarme: ON ");
lcd.setCursor(0, 1);
lcd.print("# per confermare");
schermataAggiornata = true;
}
if (tasto != NO_KEY) {
if (tasto == '#') {
allarmeAttivo = true;
digitalWrite(pinLedRosso, HIGH);
digitalWrite(pinLedVerde, HIGH);
prossimoStato = ATTESA;
cambiaStato(MSG_CONFERMATO);
} else {
prossimoStato = MENU;
cambiaStato(MSG_NON_VALIDO);
}
}
break;
// ========== CONFERMA_OFF ==========
case CONFERMA_OFF:
if (!schermataAggiornata) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" Allarme: OFF");
lcd.setCursor(0, 1);
lcd.print("# per confermare");
schermataAggiornata = true;
}
if (tasto != NO_KEY) {
if (tasto == '#') {
allarmeAttivo = false;
digitalWrite(pinLedRosso, LOW);
digitalWrite(pinLedVerde, HIGH);
noTone(pinBuzzer);
prossimoStato = ATTESA;
cambiaStato(MSG_CONFERMATO);
} else {
prossimoStato = MENU;
cambiaStato(MSG_NON_VALIDO);
}
}
break;
// ========== MSG_CONFERMATO ==========
case MSG_CONFERMATO:
if (!schermataAggiornata) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(allarmeAttivo ? " Allarme: ON " : " Allarme: OFF");
lcd.setCursor(0, 1);
lcd.print(" Confermato ");
schermataAggiornata = true;
}
if (millis() - tempoStatoInizio >= 1500) {
clearData();
cambiaStato(prossimoStato);
}
break;
// ========== MSG_NON_VALIDO ==========
case MSG_NON_VALIDO:
if (!schermataAggiornata) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" Tasto scelto ");
lcd.setCursor(0, 1);
lcd.print(" non valido ");
schermataAggiornata = true;
}
if (millis() - tempoStatoInizio >= 1500) {
cambiaStato(prossimoStato);
}
break;
// ========== CAMBIO_PWD_NEW: digita nuova password ==========
case CAMBIO_PWD_NEW:
if (!schermataAggiornata) {
clearNuovaPwd();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Nuova password:");
lcd.setCursor(0, 1);
lcd.print("______");
schermataAggiornata = true;
}
if (tasto != NO_KEY) {
if (tasto == '*') {
clearData();
cambiaStato(CAMBIO_PWD_NEW);
} else if (tasto != '#') {
nuovaPwd[pressCount] = tasto;
lcd.setCursor(pressCount, 1);
lcd.print("*");
pressCount++;
if (pressCount == 6) {
nuovaPwd[6] = '\0';
pressCount = 0;
cambiaStato(CAMBIO_PWD_NEW_INVIO);
}
}
}
break;
// ========== CAMBIO_PWD_NEW_INVIO: # per confermare ==========
case CAMBIO_PWD_NEW_INVIO:
if (!schermataAggiornata) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" '#' per INVIO");
lcd.setCursor(0, 1);
lcd.print(" '*' per CANC.");
schermataAggiornata = true;
}
if (tasto != NO_KEY) {
if (tasto == '#') {
clearData();
cambiaStato(CAMBIO_PWD_CONF);
} else if (tasto == '*') {
clearData();
cambiaStato(CAMBIO_PWD_NEW);
} else {
prossimoStato = CAMBIO_PWD_NEW_INVIO;
cambiaStato(MSG_NON_VALIDO);
}
}
break;
// ========== CAMBIO_PWD_CONF: digita conferma password ==========
case CAMBIO_PWD_CONF:
if (!schermataAggiornata) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Conferma pwd:");
lcd.setCursor(0, 1);
lcd.print("______");
schermataAggiornata = true;
}
if (tasto != NO_KEY) {
if (tasto == '*') {
clearData();
cambiaStato(CAMBIO_PWD_NEW);
} else if (tasto != '#') {
userInput[pressCount] = tasto;
lcd.setCursor(pressCount, 1);
lcd.print("*");
pressCount++;
if (pressCount == 6) {
userInput[6] = '\0';
pressCount = 0;
cambiaStato(CAMBIO_PWD_CONF_INVIO);
}
}
}
break;
// ========== CAMBIO_PWD_CONF_INVIO: # per confermare ==========
case CAMBIO_PWD_CONF_INVIO:
if (!schermataAggiornata) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" '#' per INVIO");
lcd.setCursor(0, 1);
lcd.print(" '*' per CANC.");
schermataAggiornata = true;
}
if (tasto != NO_KEY) {
if (tasto == '#') {
if (strcmp(nuovaPwd, userInput) == 0) {
salvaPassword(nuovaPwd);
clearData();
cambiaStato(MSG_PWD_SALVATA);
} else {
clearData();
cambiaStato(MSG_PWD_NO_MATCH);
}
} else if (tasto == '*') {
clearData();
cambiaStato(CAMBIO_PWD_NEW);
} else {
prossimoStato = CAMBIO_PWD_CONF_INVIO;
cambiaStato(MSG_NON_VALIDO);
}
}
break;
// ========== SIRENA: allarme scattato! ==========
case SIRENA:
if (!schermataAggiornata) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("!! INTRUSIONE !!");
lcd.setCursor(0, 1);
lcd.print("Disattiva -> '*'");
sirenaFreq = SIRENA_FREQ_MIN;
sirenaDirezione = 1;
schermataAggiornata = true;
}
aggiornareSirena();
if (tasto == '*') {
clearData();
cambiaStato(SIRENA_PWD);
}
break;
// ========== SIRENA_PWD: inserimento password ==========
case SIRENA_PWD:
if (!schermataAggiornata) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("!! INTRUSIONE !!");
lcd.setCursor(0, 1);
lcd.print("Ins. PSW: ");
schermataAggiornata = true;
}
aggiornareSirena();
if (tasto != NO_KEY) {
if (tasto == '*') {
clearData();
cambiaStato(SIRENA_PWD);
} else if (tasto != '#') {
userInput[pressCount] = tasto;
pressCount++;
lcd.setCursor(0, 1);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print("Ins. PSW:");
for (int i = 0; i < pressCount && i < 6; i++) lcd.print('*');
if (pressCount == 6) {
userInput[6] = '\0';
pressCount = 0;
cambiaStato(SIRENA_CONFERMA);
}
}
}
break;
// ========== SIRENA_CONFERMA: # per INVIO, * per CANC. ==========
case SIRENA_CONFERMA:
if (!schermataAggiornata) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" '#' per INVIO");
lcd.setCursor(0, 1);
lcd.print(" '*' per CANC.");
schermataAggiornata = true;
}
// La sirena continua a suonare
aggiornareSirena();
if (tasto != NO_KEY) {
if (tasto == '#') {
if (strcmp(userInput, master) == 0) {
allarmeAttivo = false;
noTone(pinBuzzer);
digitalWrite(pinLedRosso, LOW);
digitalWrite(pinLedVerde, HIGH);
clearData();
cambiaStato(ATTESA);
} else {
clearData();
// Mostra errore brevemente poi torna a SIRENA
cambiaStato(SIRENA_PWD_ERRATA);
}
} else if (tasto == '*') {
clearData();
cambiaStato(SIRENA);
}
}
break;
// ========== SIRENA_PWD_ERRATA: messaggio errore ==========
case SIRENA_PWD_ERRATA:
if (!schermataAggiornata) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("!! INTRUSIONE !!");
lcd.setCursor(0, 1);
lcd.print(" PWD ERRATA! ");
schermataAggiornata = true;
}
// La sirena continua a suonare
aggiornareSirena();
if (millis() - tempoStatoInizio >= 1500) {
clearData();
cambiaStato(SIRENA);
}
break;
}
}
// ==============================================================
// FUNZIONI AUSILIARIE
// ==============================================================
void cambiaStato(Stato nuovoStato) {
statoCorrente = nuovoStato;
schermataAggiornata = false;
tempoStatoInizio = millis();
}
void clearData() {
pressCount = 0;
memset(userInput, 0, PWD_LEN);
}
void clearNuovaPwd() {
memset(nuovaPwd, 0, PWD_LEN);
}
// Genera il suono sirena - chiamata da SIRENA e SIRENA_CONFERMA
void aggiornareSirena() {
if (millis() - sirenaUltimoStep >= SIRENA_STEP_MS) {
sirenaUltimoStep = millis();
tone(pinBuzzer, sirenaFreq);
sirenaFreq += sirenaDirezione;
if (sirenaFreq >= SIRENA_FREQ_MAX) sirenaDirezione = -1;
if (sirenaFreq <= SIRENA_FREQ_MIN) sirenaDirezione = 1;
}
}