#include <Wire.h> // Libreria per comunicazione I2C
#include <LiquidCrystal_I2C.h> // Libreria per il display LCD I2C
#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Keypad.h>
#include <EEPROM.h>
// Configurazione dell'LCD1602 I2C
LiquidCrystal_I2C lcd(0x27, 16, 2); // Indirizzo I2C del display LCD
// MPU6050
Adafruit_MPU6050 mpu;
// Pin per componenti hardware
const int relayPin = 14; // Pin per il relé collegato alla tromba
const int buzzerPin = 27; // Pin per il buzzer
const int keySwitchPin = 32; // Pin per la chiave di disinnesco
// Variabili di controllo
int countdownTime = 0; // Tempo impostato in secondi
int preAlertTime = 0; // Tempo di preallerta in secondi
bool bombArmed = false;
bool isTransportMode = false; // Modalità di trasporto e distruzione
bool bombDisarmed = false; // Stato della bomba (disinnescata o meno)
bool preAlertTriggered = false; // Flag per gestire l'attivazione della preallerta
// Accelerometro
float accelerationThreshold; // Soglia di accelerazione per attivare l'allerta
int sensitivityLevel = 5; // Livello di sensibilità iniziale (da 1 a 10)
float sensitivityMap[10] = {0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0, 2.5, 3.0, 4.0}; // Mappa per la sensibilità
unsigned long lastBeepTime; // Tempo dell'ultimo beep di avviso
int beepInterval = 1000; // Intervallo di beep in millisecondi (inizialmente 1 secondo)
// Definizione per tastiera 4x4
const byte ROWS = 4; // 4 righe
const byte COLS = 4; // 4 colonne
char keys[ROWS][COLS] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
byte rowPins[ROWS] = {19, 18, 17, 16}; // Pin GPIO per le righe
byte colPins[COLS] = {15, 14, 13, 12}; // Pin GPIO per le colonne
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
// Funzioni dichiarate in anticipo
void displayMenu();
void setExplosionTime();
void setPreAlertTime(); // Nuova funzione per impostare il tempo di preallerta
void startGame();
void runTimer();
void checkForDisarm();
void displayTimer(int remainingTime);
void checkAccelerometer();
void setSensitivity(); // Funzione per impostare la sensibilità
void saveSettings(); // Funzione per salvare le impostazioni
void loadSettings(); // Funzione per caricare le impostazioni
// Funzione per simulare un'esplosione usando il relè collegato alla tromba
void simulateExplosion() {
// Ciclo per simulare l'esplosione con rapidi cambiamenti del relè
for (int i = 0; i < 8; i++) {
digitalWrite(relayPin, HIGH); // Accende il relè (tromba attivata)
delay(100); // Attesa di 100 ms
digitalWrite(relayPin, LOW); // Spegne il relè (tromba disattivata)
delay(100); // Attesa di 100 ms
// Aumenta il tempo tra gli "scoppi" per simulare una diminuzione della frequenza del rumore
delay(100 * i);
}
// Un ultimo suono lungo per completare l'esplosione
digitalWrite(relayPin, HIGH);
delay(500);
digitalWrite(relayPin, LOW);
}
void setup() {
pinMode(relayPin, OUTPUT);
pinMode(buzzerPin, OUTPUT);
pinMode(keySwitchPin, INPUT); // Pin di input per l'interruttore della chiave
// Inizializza LCD1602
lcd.init();
lcd.backlight();
// MPU6050
if (!mpu.begin()) {
Serial.println("MPU6050 non trovato!");
while (1);
}
mpu.setAccelerometerRange(MPU6050_RANGE_2_G);
mpu.setGyroRange(MPU6050_RANGE_250_DEG);
Serial.begin(115200);
loadSettings(); // Carica le impostazioni salvate
// Mostra il menu iniziale
displayMenu();
}
void loop() {
if (bombArmed && !bombDisarmed) {
runTimer(); // Gestione del countdown
checkForDisarm(); // Controlla se la chiave ha disinnescato la bomba
}
}
// Funzione per visualizzare il menu e scegliere la modalità
void displayMenu() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("1: Disinnesca");
lcd.setCursor(0, 1);
lcd.print("2: Trasporto");
// Attende che l'utente selezioni una modalità
char key = keypad.getKey();
while (true) {
if (key == '1') {
isTransportMode = false; // Modalità "Disinnesca"
setExplosionTime(); // Chiede il tempo di esplosione (20-40 minuti)
setPreAlertTime(); // Chiede il tempo di preallerta (0-8 minuti)
break;
} else if (key == '2') {
isTransportMode = true; // Modalità "Trasporto e distruggi"
setExplosionTime(); // Chiede il tempo di esplosione (1-5 minuti)
break;
}
key = keypad.getKey();
}
if (isTransportMode) {
setSensitivity(); // Imposta la sensibilità solo in modalità Trasporto
}
startGame(); // Avvio del gioco dopo aver scelto la modalità
}
// Funzione per impostare il tempo di esplosione
void setExplosionTime() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Tempo: 1-5 min");
lcd.setCursor(0, 1);
String timeInput = "";
char key;
// Legge i tasti fino a quando non viene premuto 'B' per confermare
while (true) {
key = keypad.getKey();
if (key) {
if (key >= '1' && key <= '5') {
timeInput += key;
lcd.setCursor(0, 1);
lcd.print(timeInput); // Mostra l'input sulla seconda riga
} else if (key == 'B') { // Conferma con 'B'
int minutes = timeInput.toInt();
if (minutes >= 1 && minutes <= 5) {
countdownTime = minutes * 60; // Converte in secondi
break;
} else {
// Mostra messaggio di errore se il tempo è fuori dai limiti
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Errore: 1-5 min");
delay(2000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Tempo: 1-5 min");
timeInput = ""; // Resetta l'input
}
}
}
}
lcd.clear();
lcd.print("Esplosione: " + String(countdownTime / 60) + " min");
delay(2000);
}
// Funzione per impostare il tempo di preallerta
void setPreAlertTime() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Preallerta: 0-8 min");
lcd.setCursor(0, 1);
String timeInput = "";
char key;
// Legge i tasti fino a quando non viene premuto 'B' per confermare
while (true) {
key = keypad.getKey();
if (key) {
if (key >= '0' && key <= '8') {
timeInput += key;
lcd.setCursor(0, 1);
lcd.print(timeInput); // Mostra l'input sulla seconda riga
} else if (key == 'B') { // Conferma con 'B'
int minutes = timeInput.toInt();
if (minutes >= 0 && minutes <= 8) {
preAlertTime = minutes * 60; // Converte in secondi
break;
} else {
// Mostra messaggio di errore se il tempo è fuori dai limiti
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Errore: 0-8 min");
delay(2000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Preallerta: 0-8 min");
timeInput = ""; // Resetta l'input
}
}
}
}
}
// Funzione per iniziare il gioco
void startGame() {
lcd.clear();
lcd.print("Bomba Armata!");
bombArmed = true; // La bomba è ora armata
bombDisarmed = false; // La bomba non è disinnescata
preAlertTriggered = false; // Resetta il flag di preallerta
}
// Funzione per gestire il timer
void runTimer() {
unsigned long currentMillis = millis();
int remainingTime = countdownTime - (currentMillis / 1000);
// Aggiornamento del display ogni secondo
static unsigned long previousUpdateMillis = 0;
// Aggiorna il display ogni 1000 ms (1 secondo)
if (currentMillis - previousUpdateMillis >= 1000) {
previousUpdateMillis = currentMillis;
// Se la bomba è armata e non disinnescata, mostra il tempo
if (remainingTime > 0 && !bombDisarmed) {
displayTimer(remainingTime); // Mostra il countdown
// Buzzer che conteggia il tempo
if (remainingTime % 60 == 0) { // Emissione di un beep ogni minuto
digitalWrite(buzzerPin, HIGH);
delay(100); // Suona per 100 ms
digitalWrite(buzzerPin, LOW);
}
} else if (remainingTime <= 0 && !bombDisarmed) {
simulateExplosion(); // Simula l'esplosione
bombArmed = false; // Ferma il countdown
}
}
// Se è stato impostato un tempo di preallerta e non è stato ancora attivato
if (remainingTime <= (countdownTime - preAlertTime) && !preAlertTriggered && preAlertTime > 0) {
triggerPreAlert(); // Attiva l'allerta di preallerta
preAlertTriggered = true;
}
}
// Funzione per mostrare il countdown sul display
void displayTimer(int remainingTime) {
int minutes = remainingTime / 60;
int seconds = remainingTime % 60;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Tempo: ");
lcd.print(minutes);
lcd.print("m ");
lcd.print(seconds);
lcd.print("s");
}
// Funzione per gestire l'allerta di preavviso
void triggerPreAlert() {
// Attiva l'allerta e suona il buzzer
digitalWrite(buzzerPin, HIGH);
delay(1000);
digitalWrite(buzzerPin, LOW);
}
// Funzione per controllare il disinnesco della bomba
void checkForDisarm() {
if (digitalRead(keySwitchPin) == HIGH) { // Controlla se l'interruttore è attivato
bombDisarmed = true; // La bomba è disinnescata
lcd.clear();
lcd.print("Bomba Disinnescata");
delay(2000);
bombArmed = false; // Ferma il countdown
}
}
// Funzione per controllare l'accelerometro
void checkAccelerometer() {
sensors_event_t a, g, temp;
mpu.getEvent(&a, &g, &temp);
// Controlla se l'accelerazione supera la soglia
if (isTransportMode && (abs(a.acceleration.x) > accelerationThreshold || abs(a.acceleration.y) > accelerationThreshold || abs(a.acceleration.z) > accelerationThreshold)) {
// Logica per attivare l'allerta in base all'accelerometro
}
}
// Funzione per impostare la sensibilità
void setSensitivity() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Sensibilita: 1-10");
lcd.setCursor(0, 1);
char key;
String sensitivityInput = "";
// Legge i tasti fino a quando non viene premuto 'B' per confermare
while (true) {
key = keypad.getKey();
if (key) {
if (key >= '1' && key <= '9') {
sensitivityInput += key;
lcd.setCursor(0, 1);
lcd.print(sensitivityInput); // Mostra l'input sulla seconda riga
} else if (key == '0') {
sensitivityInput += '0'; // Aggiunge 0
lcd.setCursor(0, 1);
lcd.print(sensitivityInput); // Mostra l'input sulla seconda riga
} else if (key == 'B') { // Conferma con 'B'
int level = sensitivityInput.toInt();
if (level >= 1 && level <= 10) {
sensitivityLevel = level; // Aggiorna il livello di sensibilità
accelerationThreshold = sensitivityMap[sensitivityLevel - 1]; // Imposta la soglia
break;
} else {
// Mostra messaggio di errore se il livello è fuori dai limiti
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Errore: 1-10");
delay(2000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Sensibilita: 1-10");
sensitivityInput = ""; // Resetta l'input
}
}
}
}
}
// Funzione per salvare le impostazioni nell'EEPROM
void saveSettings() {
EEPROM.write(0, countdownTime / 60); // Salva il tempo di esplosione in minuti
EEPROM.write(1, preAlertTime / 60); // Salva il tempo di preallerta in minuti
EEPROM.write(2, sensitivityLevel); // Salva il livello di sensibilità
}
// Funzione per caricare le impostazioni dall'EEPROM
void loadSettings() {
countdownTime = EEPROM.read(0) * 60; // Carica il tempo di esplosione in secondi
preAlertTime = EEPROM.read(1) * 60; // Carica il tempo di preallerta in secondi
sensitivityLevel = EEPROM.read(2); // Carica il livello di sensibilità
accelerationThreshold = sensitivityMap[sensitivityLevel - 1]; // Imposta la soglia
}