#define TRIGGER_PIN 9 // Pin per il Trigger
#define ECHO_PIN 8 // Pin per l'Echo
#define BUTTON_PIN 2 // Pin del pulsante
#define BUZZER_PIN 10 // Pin del pulsante
//Pin del display
#define RS 12
#define E 11
#define D4 4
#define D5 5
#define D6 6
#define D7 7
//Variabili per misurazione distanza
volatile uint16_t startTime = 0; // Variabile per il tempo iniziale
volatile uint16_t endTime = 0; // Variabile per il tempo finale
volatile bool measurementComplete = false; // Flag per indicare che la misurazione è completa
volatile uint16_t distance;
volatile uint16_t duration;
//Variabili per inserimento codice
volatile uint8_t numero[5] = {0, 0, 0, 0, 0}; // Numero a 5 cifre
const uint8_t secretCode[5] = {1, 2, 3, 4, 5}; // Combinazione segreta a 5 cifre
volatile uint8_t posizione = 0; // Indice della cifra corrente
//Variabili per debouncing pulsante
volatile bool debounceFlag = true; // Flag di debounce
volatile uint8_t debounceCounter = 0; // Contatore debounce
//Variabili per convalidazione benvenuto
volatile bool checkFlag = false; // Flag per andare al confronto codici
volatile bool welcomeFlag = false; // Flag di benvenuto
//Variabili per stato di benvenuto
volatile bool welcomeActive = false; // Flag per indicare se il messaggio di benvenuto è attivo
volatile uint16_t welcomeCounter = 0; // Contatore per il tempo di benvenuto
//Variabili per stato di allarme
volatile bool alarmActive = false; // Flag per indicare se l'allarme è attivo
volatile uint32_t alarmCounter = 0; // Contatore per il tempo di allarme
volatile bool suspendActive = false; // Flag per indicare se il messaggio è attivo
volatile uint16_t suspendCounter = 0; // Contatore per il tempo di benvenuto
// Variabili per la cifra a rotazione
volatile uint8_t cifra = 0; //La cifra
volatile uint16_t counter = 0; // Contatore per mezzo secondo
volatile uint8_t cycles = 0; //Numero di volte che è ruotata
//Variabili per ripristino da sospensione
volatile uint8_t salvaCifra = -1; // -1 per ?
volatile uint8_t salvaCycles = -1;
//Variabili per evitare accensione ripetuta display
volatile bool displayFlag = false; // Flag per indicare se il messaggio è attivo
void setup() {
sensorSetup(); //Inizializza il sensore e il relativo timer
lcd_init(); //Inizializza lo schermo lcd
buttonSetup(); //Inizializza il pulsante e il timer di debouncing
}
void loop() {
//Misura
getDistance();
if (distance < 200) {
if(!(displayFlag)) { //Evita di accendere e spegnere ripetutamente lo schermo
lcd_displayOn();
}
timer2Setup(); //ricorda che il resto lavora con dei cicli!
while (cycles < 180) { //180 cicli * 0.5 s/ciclo = 90s
updateDisplay();
//Il checkFlag viene impostato a true solo se siamo a n.5 cifre inserite
if (checkFlag) {
checkCode();
//In base all'esito, avrò un allarme o un messaggio di benvenuto
if (welcomeFlag) {
welcome();
return;
} else {
alarm();
return;
}
}
//Effettua un'altra misurazione, se la distanza è aumentata sospendi il sistema
getDistance();
if (distance >= 200) {
suspend();
return;
}
}
//Fuori dal while, se siamo a 180 cicli siamo a 90s, il codice non è stato inserito, perciò allarme!
if (cycles >= 180) {
alarm();
return;
}
} else {
//La distanza è maggiore di 200
lcd_command(0x08); // Spegne il display
displayFlag = false; //Spegne la sua flag
}
}
void sensorSetup() {
// Impostazione dei pin 9 (TRIGGER_PIN) e 8 (ECHO_PIN) come output e input usando i registri
DDRB |= (1 << DDB1); // TRIGGER_PIN come output (pin 9)
DDRB &= ~(1 << DDB0); // ECHO_PIN come input (pin 8)
// Configuriamo il timer1 in modalità normale con prescaler 8
TCCR1A = 0; // Modalità normale
TCCR1B = (1 << CS11); // Prescaler 8, ogni tick = 0.5 µs
TCNT1 = 0; // Reset del contatore
// Abilitazione dell'interrupt PCINT su pin 8
PCICR |= (1 << PCIE0); // Abilita interrupt per il gruppo PCINT0
PCMSK0 |= (1 << PCINT0); // Abilita interrupt sul pin 8 (PCINT0)
}
// Configurazione del timer 2
void timer2Setup() {
TCCR2A = 0; // Modalità normale
TCCR2B = (1 << CS22); // Prescaler 64
TCNT2 = 0; // Reset del timer
OCR2A = 250; // Con prescaler 64, 1 tick = 4 µs; 250 * 4 µs = 1 ms
TIMSK2 = (1 << OCIE2A); // Abilita l'interrupt su confronto
}
void buttonSetup() {
// Configura il pulsante come input con pull-up
DDRD &= ~(1 << DDD2); // BUTTON_PIN come input
PORTD |= (1 << PORTD2); // Pull-up abilitato sul BUTTON_PIN
// Abilita interrupt esterno sul pulsante
EICRA |= (1 << ISC01); // Falling edge su INT0
EIMSK |= (1 << INT0); // Abilita INT0
// Configura il timer0 per generare interrupt ogni 200ms
TCCR0A = 0; // Modalità normale
TCCR0B = (1 << CS01) | (1 << CS00); // Prescaler 64, 1 tick = 4 microsecondi
OCR0A = 250; // Imposta il valore di confronto per 1 ms (calcolato)
TIMSK0 |= (1 << OCIE0A); // Abilita l'interrupt per il timer0
}
void getDistance() {
if (!measurementComplete) {
// Genera il segnale di Trigger
PORTB &= ~(1 << PORTB1); // Imposta TRIGGER_PIN su LOW
//delayMicrosecondsTimer1(2);
PORTB |= (1 << PORTB1); // Imposta TRIGGER_PIN su HIGH
//delayMicrosecondsTimer1(10);
PORTB &= ~(1 << PORTB1); // Riporta TRIGGER_PIN su LOW
// Aspetta il completamento della misurazione
while (!measurementComplete); // Blocco finché il flag non è vero
}
// Calcola la durata e la distanza
duration = (endTime - startTime) / 2;
distance = (duration * 34300) / 1000000 / 2;
// Resetta il flag per la prossima misurazione
measurementComplete = false;
}
/*
void delayMicrosecondsTimer1(uint16_t us) {
uint16_t targetTicks = (us * 2); // Con prescaler 8: 1 tick = 0.5 µs
TCNT1 = 0; // Resetta il timer
while (TCNT1 < targetTicks); // Attendi fino al raggiungimento dei tick desiderati
}
*/
void lcd_init() {
// Imposta i pin come OUTPUT
DDRD |= (1 << PD4); // D4 (pin 4) come uscita
DDRD |= (1 << PD5); // D5 (pin 5) come uscita
DDRD |= (1 << PD6); // D6 (pin 6) come uscita
DDRD |= (1 << PD7); // D7 (pin 7) come uscita
DDRB |= (1 << PB4); // RS (pin 12) come uscita (sulla porta B, pin 0)
DDRB |= (1 << PB3); // E (pin 11) come uscita (sulla porta B, pin 1)
lcd_command(0x08); // Spegne il display
}
void lcd_displayOn() {
displayFlag = true;
// Inizializza il display in modalità 4 bit
lcd_command(0x02); // Imposta modalità 4 bit
lcd_command(0x28); // 2 linee, 5x8 font
lcd_command(0x0C); // Display acceso, cursore spento
lcd_command(0x06); // Incremento del cursore
lcd_command(0x01); // Pulisce il display
}
// Funzione per inviare un comando al display
void lcd_command(byte cmd) {
PORTB &= ~(1 << PB4); // Imposta RS (pin 12) a LOW (porta B, pin 4)
lcd_sendNibble(cmd >> 4); // Invia i primi 4 bit
lcd_sendNibble(cmd & 0x0F); // Invia i secondi 4 bit
}
// Funzione per scrivere un carattere sul display
void lcd_write(byte data) {
PORTB |= (1 << PB4); // Imposta RS (pin 12) a HIGH (porta B, pin 4)
lcd_sendNibble(data >> 4); // Invia i primi 4 bit
lcd_sendNibble(data & 0x0F); // Invia i secondi 4 bit
}
// Funzione per inviare i dati al display
void lcd_sendNibble(byte data) {
// Maschera e invia i 4 bit
//digitalWrite(D4, (data >> 0) & 1); // D4
PORTD = (PORTD & ~(1 << PD4)) | (((data >> 0) & 1) << PD4);
//digitalWrite(D5, (data >> 1) & 1); // D5
PORTD = (PORTD & ~(1 << PD5)) | (((data >> 1) & 1) << PD5);
//digitalWrite(D6, (data >> 2) & 1); // D6
PORTD = (PORTD & ~(1 << PD6)) | (((data >> 2) & 1) << PD6);
//digitalWrite(D7, (data >> 3) & 1); // D7
PORTD = (PORTD & ~(1 << PD7)) | (((data >> 3) & 1) << PD7);
// Attiva l'abilitazione (E) per 1 ms per inviare il dato
PORTB |= (1 << PB3); // Imposta E (pin 11) a HIGH
PORTB &= ~(1 << PB3); // Imposta E (pin 11) a LOW
}
void updateDisplay() {
lcd_command(0x80); // Posiziona il cursore all'inizio della prima riga
lcd_write('0' + cifra); // Scrive la cifra sul display
lcd_command(0xC0); // Posiziona il cursore all'inizio della seconda riga
for (uint8_t i = 0; i < 5; i++) {
lcd_write('0' + numero[i]); //Stampa il codice inserito carattere per carattere
}
}
void checkCode() {
//Punteggio, se c=5 allora tutti i numeri fanno un match
int c = 0;
for (int i = 0; i < 5; i++) {
if (!(numero[i] == secretCode[i])) {
//Almeno 1 confronto non combaciante, ci sarà un allarme
welcomeFlag = false;
checkFlag = false;
} else c++; //Incrementa il punteggio
}
//Tutti match, welcomeFlag pronta
if (c == 5) {
welcomeFlag = true;
checkFlag = false;
}
}
void welcome() {
lcd_command(0x01); // Pulisce il display
lcd_command(0x80); // Posiziona il cursore all'inizio
lcd_write('W'); // Scrive "Benvenuto"
lcd_write('e');
lcd_write('l');
lcd_write('c');
lcd_write('o');
lcd_write('m');
lcd_write('e');
lcd_write('!');
welcomeFlag = false; //E' già passato in stato welcome
welcomeActive = true; // Attiva il flag
cycles = 0; //Azzera i cicli
timer2Setup(); //Reset timer2
while (welcomeActive == true); //Attende il termine del benvenuto
resetNumbers(); //Resetta le variabili dei numeri
}
void alarm() {
lcd_command(0x01); // Pulisce il display
lcd_command(0x80); // Posiziona il cursore all'inizio
lcd_write('A'); // Scrive "Benvenuto"
lcd_write('l');
lcd_write('a');
lcd_write('r');
lcd_write('m');
lcd_write('!');
cycles = 0; //Azzera i cicli
alarmActive = true; // Attiva il flag
timer2Setup(); //Reset timer2
PINB |= (1 << PINB2); //Attiva il buzzer
while (alarmActive); //Attende il termine dell'allarme
PINB &= ~(1 << PINB2); // Riporta BUZZER_PIN su LOW
//Alla fine dell'allarme ricontrolla la distanza, se è rimasta minore di 200 esegui allarmi ricorsivamente
getDistance();
if(distance < 200) {
alarm();
}
resetNumbers(); //Reset variabili dei numeri
}
//Reset generale variabili dei numeri
void resetNumbers() {
for (int i = 0; i < 5; i++) {
numero[i] = 0;
}
cifra = 0;
cycles = 0;
}
void suspend() {
updateDisplay(); //Mostra il display con le ultime variabili salvate
suspendActive = true; // Attiva il flag
timer2Setup(); //Reset timer2
salvaCifra = cifra; //Salva il numero sullo schermo
salvaCycles = cycles; //Salva il numero della rotazione
while (suspendActive) { //Finchè è sospeso
getDistance();
if(distance < 200) { //Se la distanza è maggiore di 200
cycles = salvaCycles; //RIpristina e resetta le variabili
cifra = salvaCifra;
salvaCifra = -1;
salvaCycles = -1;
counter = 0; //Per la rotazione
suspendActive = false; //Non è più sospeso
return;
}
}
resetNumbers(); //Resetta variabili numeriche
}
//---------------------------------------ISR---------------------------------------------
//Gestione cambiamento di stato del pin ECHO_PIN
ISR(PCINT0_vect) {
if (PINB & (1 << PINB0)) {
// Rising edge: salva il tempo iniziale - ECHO_PIN è in ascolto
TCNT1 = 0; // Resetta il contatore del timer
startTime = TCNT1; // Salva il valore del timer
} else {
// Falling edge: salva il tempo finale - ECHO_PIN ha rilevato il segnale
endTime = TCNT1; // Salva il valore del timer
measurementComplete = true; // La misurazione è completa
}
}
/*Gestione rotazione numerica, timer 90s per inserimento codice, timer di benvenuto,
timer di allarme, timer di sospensione
*/
ISR(TIMER2_COMPA_vect) {
if (!(suspendActive) && (distance < 200)) {
counter++;
if (counter >= 500) { // 500 ms
counter = 0;
cifra = (cifra + 1) % 10; // Incrementa la cifra (da 0 a 9)
cycles++;
}
}
// Gestione del tempo del messaggio di benvenuto
if (welcomeActive) {
welcomeCounter++;
if (welcomeCounter >= 5000) { // 5000 ms = 5 secondi
welcomeActive = false; //Abbassa la flag
lcd_command(0x01); // Pulisce il display
welcomeCounter = 0; // Resetta il contatore
}
}
if (alarmActive) {
alarmCounter++;
if(alarmCounter >= 300000) { //300s = 5 minuti
alarmActive = false;
lcd_command(0x01); //Pulisce il display
alarmCounter = 0; // Resetta il contatore
}
}
if (suspendActive) {
suspendCounter++;
if(suspendCounter >= 60000) {
suspendActive = false; //Abbassa la flag
lcd_command(0x01); //Pulisce il display
suspendCounter = 0; // Resetta il contatore
}
}
}
// ISR per INT0 (interrupt del pulsante, acquisizione cifra e richiesta di validazione)
ISR(INT0_vect) {
if (debounceFlag && !(suspendActive) && !(welcomeActive) && !(alarmActive)) { // Verifica se è passato il tempo di debounce e se può acquisire input
numero[posizione] = cifra; // Aggiorna il numero
if (posizione == 4) { //Se la posizione è la 4, abbiamo riempito l'inserimento del codice
checkFlag = true;
}
posizione = (posizione + 1) % 5; // Passa alla cifra successiva
debounceFlag = false; // Resetta il flag di debounce
debounceCounter = 0; // Contatore
}
}
// Funzione ISR per il timer0 (debounce)
ISR(TIMER0_COMPA_vect) {
debounceCounter++;
if (debounceCounter > 200) { //200ms tempo medio debounce
debounceCounter = 0;
debounceFlag = true; // Segna che è passato il tempo di debounce
}
}