Fermare una funzione

Salve a tutti, sto scrivendo il codice per un'allarme e vorrei che se si entra nel menù per inserire la password e dopo 10 secondi non si preme più nessun tasto l'arduino interrompa l'esecuzione della funzione che permette l'inserimento della password e torni ad eseguire il loop.
Qui sorge il problema perche se uso break arduino ritorna nel loop ma considerando la password corretta e quindi abilitando o disabilitando l'allarme.
Qualcuno sa come potrei fare?

Difficile da dire senza vedere il tuo codice.
Posta il codice attuale (racchiudilo tra i tag "code" cliccando sul pulsantino "<CODE/>" dell'editor) così vediamo di capire meglio e darti qualche consiglio più specifico.

Se usaste un approccio basato su una macchina a stati (qui una piccola introduzione in inglese Yet another Finite State Machine introduction) non saresti costretto a restare bloccato nella funzione, il loop continuerebbe a girare e un evento di timeout farebbe uscire dalla modalità di inserimento della password.

Se invece resti con il tuo approccio, la tua funzione potrebbe restituire un valore booleano che indica se la password è stata inserita (return true;) oppure no (return false;), e la funzione chiamante controllerebbe questo valore di ritorno per decidere cosa fare.

1 Like

Hai ragione ero convinto di averlo inserito sopra :sweat_smile:

#include <Keypad.h>
#include <LiquidCrystal_I2C.h>


LiquidCrystal_I2C lcd(0x27, 16, 2);


//Variable
bool passc = true;
bool ins = false;
const unsigned long durata = 5000;
long noRetro = 0;
unsigned long startTime = 0;
bool condizione;
bool reset;
bool canSound = false;
char key;
const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns
char keys[ROWS][COLS] = {
  {'1','2','3','a'},
  {'4','5','6','b'},
  {'7','8','9','c'},
  {'*','0','#','d'}
};
byte rowPins[ROWS] = {9, 8, 7, 6}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {5, 4, 3, 2}; //connect to the column pinouts of the keypad
 
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
 
 
char password[5] = "b030";//password 
char tempPassword[5];
int tempPasswordIndex = 0;



void pass(){
 
  key = keypad.getKey();
  if (ins){
    scatto();
    scattopre();
  }
  if (key){
    noRetro = millis();
    lcd.backlight();
    tempPassword[tempPasswordIndex] = key; //reading password
    tempPasswordIndex++;
    lcd.setCursor(tempPasswordIndex - 1,1);
    lcd.print(F("*"));
    tone(1,1200);
    delay(20);
    noTone(1);
    if(tempPasswordIndex>4) //4 char for password
    {
      tempPasswordIndex = 0;
      if(checkPassword()) //if true...
      {
        lcd.clear();
        lcd.setCursor(4, 1);
  		  lcd.print(F("DifAlarm"));
        delay(500);
        lcd.setCursor(3,0);
        lcd.print(F("Password V"));
        passc = false;
        reset = false;
        condizione = false;
        delay(500);
        lcd.clear();
        

      }
      else  //if false...
      {
        lcd.clear();
        lcd.setCursor(4, 1);
  		  lcd.print(F("DifAlarm"));
        delay(500);
        lcd.setCursor(3,0);
        lcd.print(F("Password X"));
        delay(500);
        lcd.clear();
        lcd.setCursor(0, 0);
    	  lcd.print(F("Password"));
      }
    }
  }
  delay(50);

  
}

 

 
 
boolean checkPassword(){ // checking password

  if(strncmp(password, tempPassword, 4) == 0)
  {
    return true;
  }
  else {   
  return false;
  }
}

void controllo(){
  lcd.setCursor(14, 0);
  if(digitalRead(13) == HIGH){
    lcd.print(F("V"));
  }
  else if(digitalRead(13) == LOW){
    lcd.print(F("X"));
  }
  lcd.setCursor(15, 0);
  if(digitalRead(12) == HIGH){
    lcd.print(F("V"));
  }
  else if(digitalRead(12) == LOW){
    lcd.print(F("X"));
  }
}

void retro(){
  if (digitalRead(11) == LOW){
    if (millis() - noRetro >= 5000) {
      lcd.noBacklight();
    }
  }
  else{
    lcd.backlight();
  }
}

void stato(){
  lcd.setCursor(4, 1);
  lcd.print(F("DifAlarm"));
  if(ins == false && reset == false){
    digitalWrite(11,LOW);
    lcd.setCursor(0,0);
    lcd.print(F("OFF"));
  }
  else{
    lcd.setCursor(0,0);
    lcd.print(F("ON"));
    scatto();
    scattopre();
  }
}

void scatto(){
  if(digitalRead(13) == LOW){
    lcd.setCursor(13,0);
    lcd.print(F("ALM"));
    digitalWrite(11,HIGH);
  }
  delay(20);
}

void scattopre(){
  if (condizione == false){
  	condizione = digitalRead(12) == LOW;
    canSound = true;
  }

  if (condizione) {
    if (canSound){
      tone(1, 1200);
      delay(50);
      noTone(1);
      delay(50);
  	}
    reset = true;
      if (startTime == 0) {
          startTime = millis(); // Memorizza il tempo di inizio
      }
        
      if (millis() - startTime >= durata) {
        lcd.setCursor(13,0);
    	  lcd.print(F("ALM"));
        digitalWrite(11,HIGH);
        canSound = false;
        noTone(1);
      }
  }


}
void setup() {
  pinMode(13,INPUT);
  pinMode(12,INPUT);
  pinMode(11,OUTPUT);
  pinMode(1,OUTPUT);


  lcd.init(); //Init with pin default ESP8266 or ARDUINO
  lcd.backlight(); //accende la retroilluminazione
  delay(700);
  lcd.noBacklight();
  delay(700);
  lcd.backlight();
  delay(700);
  lcd.setCursor(4, 1);
  lcd.print(F("DifAlarm"));
  delay (1000);
  lcd.setCursor(0, 0);
  lcd.print(F("Inizializzazione"));
  delay (1500);
  lcd.clear();
  noRetro = millis();

}



void loop(){
  stato();
  retro();
  char key = keypad.getKey();
  if(key){
    noRetro = millis();
    lcd.backlight();
  }

  if (key == '#' && ins == false){
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print(F("Password"));
    passc = true;
    while(passc){
    	pass();
      controllo();
      retro();
    }
    lcd.clear();
    lcd.setCursor(1,0);
    lcd.print(F("Inserimento..."));
    //delay(5000);
    noRetro = millis();
    ins = true;
    lcd.clear();
  }
  
  else if (key == '#' && ins == true){
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print(F("Password"));
    passc = true;
    while(passc){
    	pass();
      retro();
      canSound = false;
      noTone(1);
    }
    ins = false;
  }
  if (digitalRead(12) == HIGH && reset == false) {
   	startTime = millis(); // Resetta il timer se la condizione diventa falsa
  }

}

Questo è il codice praticamente vorrei che dopo aver premuto il tasto '#' passati 10 secondi se non si preme alcun tasto l'arduino ritorni ad eseguire il loop.

Non sono sicuro poiché non riesco a seguire il codice. Quindi il 'credo' va messo davanti ad ogni affermazione che farò a seguire.
Prima di entrare nel ciclo while(passc) salva millis() in una variabile di nome
passwdTimeout dichiarata globale.

    passc = true;
    passwdTimeout  = millis();
    while(passc) {
    	pass();
        controllo();
        retro();
        if (millis() - passwdTimeout >= 10000) { // [1]
            passc = false;
        }
    }

[1] - Questa if potrebbe pure essere messa all'interno di una delle funzioni
chiamate all'interno del corpo del while. Potrebbe benissimo essere messa nella funzione pass().

void pass(){
 
  key = keypad.getKey();
  if (ins){
    scatto();
    scattopre();
  }
  if (key){
      passwdTimeout = millis();  // ricarica il tempo  di riferimento salvato in passwdTimeout;

La password

Solitamente, si entra in modalità richiesta password al fine di consentire
una operazione consentita solo a chi possiede la password. In questa modalità ci dovrebbe essere la possibilità di premere un pulsante che ha la
funzione del tasto "ESC", che se premuto esce dalla modalità richiesta password di fatto annullando la richiesta. Chi inserisce la password corretta ha accesso ad una sessione nella quale può eseguire operazioni riservate, si rimane in questa sessione per un tempo stabilito, superato questo tempo
si torna nella sessione non privilegiata.

PS: retro, stato, scattopre, scatto avranno un significato per te, ma agli altri
programmatori non danno informazioni utili per seguire la logica del programma. Ad esempio 'retro' la parte posteriore di un qualcosa. scattopre; sarà l'istante precedente in cui ci si prepara a scattare una foto, booh.
Ciao.

Chiarisci meglio quello che chiedi: mentre si inserisce la password confermi che il codice non deve fare altro fino a che non venga inserita correttamente (o siano passati 10 secondi come timeput di inserimento)? Perché attualmente il tuo codice pare che sia impostato per fare questo.
In ogni caso nonostante tu abbia fatto correttamente funzioni separate per ogni elemento funzionale, ho provato ma il tuo codice non è molto semplice da leggere proprio perché "mescola" le cose.
Ad esempio ci sono chiamate a "scatto()" e "scattopre()" anche in "pass()": visto che la "pass()" serve solo a verificare la password, dato che la richiami nel loop dentro ad un ciclo while, mettile qui dentro e non in "pass()". In pratica la prima cosa che ti consiglio è di ricordare di inserire in ogni funzione tutte e sole le istruzioni che riguardano ciò che questa deve gestire, senza "spargere" operazioni che non riguardano l'oggetto quindi "pass" deve gestire la sola password, "scatto" gli scatti, eccetera.

L'altro consiglio è quello di chiarire sempre a te stesso meglio cosa il programma debba fare (il "cosa" più che "come"), magari PRIMA di iniziare a scrivere il codice. Diciamo i tuoi "requisiti", che magari potresti scrivere come appunti in un file di testo (anche io faccio così). Da questi "requisiti" poi iniziare a svilupparli ulteriormente scrivendoli nel codice sotto forma di commenti, che poi man mano vai a sviluppare scrivendogli sotto il relativo codice. Fidati, che questo metodo semplifica molto lo sviluppo.

Poi (e comunque legato al discorso precedente) nel tuo codice qui non vedo che pochissimi commenti (tra l'altro spesso in inglese, segno che forse sono parti di codice che hai "copiato" da qualche parte e "assemblato" qui): i commenti non sono una cosa superflua perché intanto aiutano te stesso sia a strutturare meglio il codice sia a recuperare la sua logica se metti mano ad un tuo sketch scritto magari un anno prima, ma poi se dovessi postarlo qui per chiedere aiuto, potremmo capire meglio cosa volevi fare in certe sezioni, senza costringerci a cercare di "dedurlo" dalle istruzioni e dalla tua descrizione del problema.

Poi veniamo al discorso del bloccare il codice. In questo caso il sospetto è che il codice non debba necessariamente fare solo una cosa per volta e "bloccarsi" mentre ad esempio chiede la password, ma che comunque ci siano altre cose. Per fare questo dovresti apprendere il concetto di "macchina a stati finiti" o "FSM" in inglese "finite State Machine" di cui @J-M-L ti ha accennato. Questo perché su sistemi di controllo (ed Arduino lo è di fatto) è una pratica molto comune perché consente di avere un codice non solo "snello" (perché ad esempio evita cose come quelle chiamate a "scatto()" in una funzione che deve gestire altro) e più leggibile (perché vedrai che sarà tutto più chiaro a te ed a noi una volta che l'avrai assimilato ed implementato) ma consente anche di fare più cose "contemporaneamente" (tra virgolette, perché di fatto sono cose che gestisce con un loop() che gira migliaia di volte al secondo per cui sembrano tutte contemporanee). Tempo fa (nel lontano 2020) scrissi qui nel forum una descrizione in italiano delle FSM ed anche una piccola libreria (non è necessario che tu debba usare questa, ma la scrissi per me perché alcune di quelle che provai non mi soddisfacevano), qui trovi tutto:

Ovviamente tutto questo richiederebbe una revisione del tuo codice non proprio banale. Soprattutto per noi, visto che lo sketch attuale è appunto strutturato non proprio chiaramente e non ci sono commenti,

Per cui ti chiederei in primo luogo di provare ad inserire commenti nell'attuale sketch per spiegare cosa fa ogni funzione (e al loro interno magari spiegare ad esempio le condizioni delle "if()" e altro codice che fa "qualcosa" che non è immediatamente intuibile). Ma dovresti anche chiarirci meglio la tua domanda: nel codice non c'è nessun "break", cosa intendi con "arduino ritorna nel loop ma considerando la password corretta"? Dove pensavi di mettere questo "break" e perché? Potresti metterlo dove lo avevi messo, aggiungere qualche commento in più e postare il "nuovo" codice? Magari possiamo intanto risolverti questo problema, per la trasformazione in FSM ci potrai pensare successivamente.

Ti ringrazio molto per il tuo aiuto, però la tua soluzione non funziona perché alla fine l'allarme si inserisce, visto che la variabile "ins" viene comunque impostata su true.

 while(passc){
      pass();
      controllo();
      retro();
    }
    lcd.clear();
    lcd.setCursor(1,0);
    lcd.print(F("Inserimento..."));
    //delay(5000);
    noRetro = millis();
    ins = true; //qui la variabile che determina l'inserimento viene impostata su true
    lcd.clear();

Su questo ti do ragione pienamente, forse è perché sono abituato a lavorare da solo e quindi ad esempio scrivendo void retro() mi risale subito in mente che quella funzione serve a gestire lo spegnimento e l'accensione della retro illuminazione del display.
Probabilmente se avessi scritto void retro() //gestisce la retro illuminazione del display sarebbe stato più chiaro.

Non esattamente se l'allarme è inserito (ins == true) l'arduino deve chiamare anche le funzioni scatto() e scattopre(), cosa che però non ho detto effettivamente :sweat_smile:.

Anche su questo hai ragione infatti poi in seguito mi sono accorto anche io di questa cosa e l'ho corretta.

Lo so solo che mentre scrivo il programma mi vengono in mente altre idee e cose da aggiungere al programma e alla fine finisco per scrive un codice funzionante ma scritto "male".

Anche questo è vero non ho mai preso l'abitudine di inserire commenti li inserisco solo come promemoria di quello che stavo facendo quando prendo una pausa, per il fatto dell'inglese è solo che sono abituato a dare nomi alle variabili o comunque scrivere commenti in inglese perché lo parlo abbastanza "bene" quindi mi viene spontaneo.

Pensavo di scrivere:

if(key == '#'){
   passwdTimeout = millis();
   passc = true;
   while(passc){
        pass();
        controllo();
        retro();
        if (millis() - passwdTimeout >= 10000) {
            break;
     }
      lcd.clear();
      lcd.setCursor(1,0);
      lcd.print(F("Inserimento..."));
      //delay(5000);
      noRetro = millis();
      ins = true; //qui la variabile che determina l'inserimento viene impostata su true
      lcd.clear();
}

Però utilizzando break l'allarme viene inserito perche la variabile "ins" viene impostata su true.
Comunque vedrò di aggiungere più commenti al codice così che sia più comprensibile :slightly_smiling_face:

Bella fregatura. Pensa te, io ho letto (e studiato) più codice altrui di quanto
ne ho scritto e ti posso assicurare che se c'è di mezzo una tastiera c'è una variabile di nome ins, se è true la modalità inserimento della tastiera è attiva, diversamente non è attiva. Quindi il nome ins è il peggiore che potessi scegliere. bool mainAlarmState o bool globalAlarmState oppure trovalo tu un nome di variabile che non possa essere frainteso.

Comunque, in quale modo attivi l'allarme e in quale modo lo disattivi;
Se usi sempre la stessa procedura è un bene per l'utente dell'impianto.
mainAlarmState is(true) - che significa che l'allarme è inserito
Ora voglio disinserirlo:
Premo #, che mi invita a scrivere la password se questa è corretta
mainAlarmState = !mainAlarmState; Inverte lo stato dell'allarme
Se passano 10 secondi e la password non è stata inserita o è stata inserita ma scorretta lascia tutto come stava prima.

Ciao.

E allora perché non scrivi anche nomi variabili e nomi funzioni in inglese?
Se aggiungi i commenti come ti hanno già suggerito, il tuo codice sarebbe molto più leggibile (consiglio: prova a chiedere ad una AI di farlo per te, se scrivi in modo esplicito quello che deve fare la funzione e i nomi delle variabili, di solito fanno un buon lavoro e ci azzeccano con i commenti).

Per il resto, io mi associo a chi ti ha già consigliato una macchina a stati finiti o comunque di seguire un approccio non bloccante in linea generale.

Non capisco perché quando stai inserendo il codice da tastierino, blocchi l'esecuzione in un while, ti basterebbe tenere traccia del fatto che stai inserendo (lo "stato"), contare il numero dei tasti che sono stati premuti e quando è uguale alla lunghezza del codice impostato vai a verificarne la correttezza e resetti tempPasswordIndex a prescindere.

Se devi fare "cose" mentre viene inserito il codice ti basta verificare che tempPasswordIndex != 0`.

E quindi il codice oltre a non funzionare probabilmente come dovrebbe, diventa talmente "incasinato" da risultare poco leggibile persino per te che lo hai scritto (pensa anche a noi...) e da questo diventa difficilissimo anche fare debug!

Evita di aggiungere cose "di getto": fai una cosa per volta iniziando da una struttura "base" del codice (iniziando prima solo con i commenti come ti ho consigliato!) che implementa le funzioni A e B, e poi quando funziona per A e B salva da qualche parte quella versione 1.0, ed inizia a pensare a C e come si potrebbe inserire in una versione 1.1, poi la D per una 1.2, e così via.
Il ciclo deve essere "studiare, analizzare, descrivere (a te stesso con un testo di requisiti o almeno come commenti), implementare, testare, salvare" da ripetere per ogni nuova funzionalità che vuoi aggiungere.

Perdonami ma ancora non ho capito dove debba stare il "break" (forse intendevi "return" per tornare dalla funzione "pass()"?), e quale debba essere esattamente il comportamento desiderato (che posso solo immaginare, sapendo che si tratta di un antifurto), comunque se la variabile "ins" rappresenta l'inserimento del sistema, all'inserimento corretto della password penso che vorresti modificare questo stato ossia se è allarmato lo disarma, è così?
Perché in tal caso dato che "ins" è una variabile globale, ti basta disattivarla quando l'inserimento è avvenuto correttamente, ad esempio:

    if(tempPasswordIndex>4) //4 char for password
    {
      tempPasswordIndex = 0;
      if (checkPassword()) //if true...
      {
        ins = false; // questo???
        lcd.clear();
        lcd.setCursor(4, 1);
        lcd.print(F("DifAlarm"));
...

Ma comunque sia, ripeto, indenta bene il codice (Ctrl-T nell'IDE) poi inizia ad inserire commenti che descrivono cosa fa ogni funzione ed ognuno dei blocchi, ad esempio non:

//Variable
bool passc = true;
bool ins = false;

(visto che "Variable" non significa niente, e non descrivi cosa rappresentano quelle variabili) ma ad esempio una cosa tipo:

// Se true significa che accetta l'inserimento della password
bool passc = true;
// Se true indica che l'allarme è inserito
bool ins = false;

Lo stesso per le varie funzioni, metti un commento all'inizio che descrive cosa fa, quali eventuali parametri richiede, quali variabili globali usa, e cosa eventualmente restituisce, ad esempio:

// Gestisce la retroilluminazione spegnendola dopo 5 secondi
void retro(){
  if (digitalRead(11) == LOW){
    if (millis() - noRetro >= 5000) {
      lcd.noBacklight();
    }
  }
  else{
    lcd.backlight();
  }
}

Quindi ora fai le tue modifiche per cercare di far andare il codice come lo stai pensando, e posta qui il risultato se hai ancora problemi.

Tutto ciò sempre fermo restando che queste cose bisognerebbe imparare a strutturarle diversamente quantomeno con una macchina a stati finiti, ma anche se per ora te lo risparmio devi prendere in considerazione di studiare le FSM iniziando dal link che ti ho indicato, o anche da vari tutorial in rete tipo questo ad esempio per iniziare:

Ho un tutorial in francese sulla gestione delle comunicazioni asincrone (porta seriale, tastierino, ...) con un esempio per un inserimento non bloccante dal tastierino. Forse potrebbe interessarti (con Google Translate).

OFF-TOPIC - on

@J-M-L, toglimi una curiosità.... ma quante lingue sei in grado di parlare? :face_with_crossed_out_eyes:

OFF-TOPIC - off
OFF-TOPIC - on

Parlo bene il francese e l’inglese (e correttamente il latino e il greco antico, ma è difficile parlare di elettronica e programmazione in queste lingue morte :) ).

Inoltre pratico e cerco di migliorare in tedesco, italiano, svedese e olandese.

OFF-TOPIC - off
3 Likes

Ho riscritto il codice completandolo, infatti ora è funzionante, e ho inserito i commenti, questa volta spero sia comprensibile.

allarme.ino (16,2 KB)

Lo allego come file perché "<CODE/>" fa casini con l'indentazione, inoltre allego anche una simulazione con "Wokwi".

Dato che ci sono parecchi cavi ti risparmio il "raccapezzamento" dei circuiti e ti allego un'immagine dove è scritto cosa fanno i vari circuiti.

La password è "b030" e un qualsiasi tasto (tranne '#') per confermare.

Ti ringrazio per il tuo esempio, lo terrò a mente per un mio futuro progetto :slightly_smiling_face:

1 Like

E per qual motivo? (cit. Aldo Giovanni e Giacomo, Busto Garolfo Cops) :smiling_face_with_sunglasses:
Al contrario, selezionando con "CODE" il codice ne mantiene esattamente l'indentazione (oltre ad altre cose molto utili, come la colorazione automatica, la finestra con scroll, e la possibilità di copiare l'intero testo con un click)...
Praticamente viene così, quale casino farebbe "CODE"?

#include <Keypad.h>
#include <LiquidCrystal_I2C.h>

//inizializza l'lcd 16 colonne 2 righe all'indirizzo 0x27
LiquidCrystal_I2C lcd(0x27, 16, 2);


//Variable
bool passc = true;//se true accetta l'inserimento della password per inserire/disinserire l'allarme
bool ins = false;//se true indica che l'allarme è inserito
bool home = true;//se l'arduino ritorna nel loop dopo che la password è corretta inserisce l'allarme
bool man = false;//se true indica che lo stato di manutenzione è attivo
bool almMan = false;//indica se l'allarme è scattato a causa dei sensori anti-manomissione
bool passmanc = false;//se true accetta l'inserimento della password per inserire/disinserire la modalità di manutenzione
bool hardReset = false;//se true indica ad arduino di resettarsi
const unsigned long durata = 15000;//tempo dopo il quale l'allarme scatta se viene aperto il circuito del pre allarme (pin 12 LOW)
const unsigned long durataIns = 10000;//tempo dopo il quale l'arduino ritorna nel menu principale se non viene premuto alcun tasto
long noRetro = 0;//valore che si aggiorna dopo ogni pressione di un tasto (gestione retro illuminazione)
unsigned long startTime = 0;
long tempIns = 0;//valore che si aggiorna dopo ogni pressione di un tasto quando si inserisce la password
bool condizione;//viene aggiornata con allarme inserito con il valore logico del pin 12
bool reset;//indica al timer del pre-allarme se può resettarsi
bool canSound = false;//indica se il buzzer può suonare
char key;//contiene il carattere premuto sul keypad
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] = {9, 8, 7, 6}; //pin righe keypad
byte colPins[COLS] = {5, 4, 3, 2}; //pin colonne keypad
 
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );//keymap
 
 
char password[5] = "b030";//password 
char tempPassword[5];//contiene la password inserita dall'utente
int tempPasswordIndex = 0;//contiene l'ndice della password inserita dall'utente


//funzione inserimento password per inserimento/disinserimento allarme
void pass(){
 
  key = keypad.getKey();//memorizza il tasto premuto sul keypad
  //ritorna nel menu principale se non si preme un tasto da piu del tempo inserito in durataIns o se si preme il tasto '#'
  if (millis() - tempIns >= durataIns || key == '#') {
    passc = false;//non permette l'inserimento della password 
    home = false;//fa in modo che non venga inserito l'allarme
    hardReset = false;//reimposta la variabile per il reset della scheda
    lcd.clear();
    //resetta la password
    for(int i; i >= 5; i++){
      tempPassword[i] = "";
    }
    tempPasswordIndex = 0;
  }
  //se il tasto premuto non è '#'
  else if (key){
    noRetro = millis();//salva quando è stato premuto il tasto
    tempPassword[tempPasswordIndex] = key; //legge la password
    tempPasswordIndex++;//aumenta l'indice di 1
    lcd.setCursor(tempPasswordIndex - 1,1);//imposta il cursore sul display 
    lcd.print(F("*"));//stampa il carattere '*' sull'lcd
    tone(1,1200);
    delay(20);
    noTone(1);
    if(tempPasswordIndex>4) //controlla se l'indice password è maggiore di 4
    {
      tempPasswordIndex = 0;//resetta l'indice della password
      if(checkPassword()) //se la password è corretta
      {
        //indica all'utente che la password è corretta con una scritta sull'lcd
        lcd.clear();
        lcd.setCursor(4, 1);
  		  lcd.print(F("DifAlarm"));
        delay(500);
        lcd.setCursor(3,0);
        lcd.print(F("Password V"));
        delay(500);
        passc = false;//esce dal ciclo di inserimento password
        reset = false;//indica al timer del pre-allarme che può resettarsi
        condizione = false;//questa riga serve nella funzione scattopre() per far si che legga lo stato logico del pin 12
        //se si è entrati nella funzione con il tasto 'd' resetta la scheda
        if (hardReset){
          lcd.clear();
          lcd.setCursor(4, 1);
  		    lcd.print(F("DifAlarm"));
          lcd.setCursor(4, 0);
          lcd.print(F("Reset"));
          delay(150);
          lcd.print(F("."));
          tone(1, 1200);
          delay(50);
          noTone(1);
          delay(250);
          lcd.print(F("."));
          tone(1, 1200);
          delay(50);
          noTone(1);
          delay(250);
          lcd.print(F("."));
          tone(1, 1200);
          delay(200);
          digitalWrite(0,HIGH);//attiva il rele collegato sul reset
        }
        lcd.clear();
        

      }
      else  //se la password è sbagliata
      {
        //indica all'utente che la password è sbagliata con una scritta sull'lcd
        lcd.clear();
        lcd.setCursor(4, 1);
  		  lcd.print(F("DifAlarm"));
        delay(500);
        lcd.setCursor(3,0);
        lcd.print(F("Password X"));
        delay(500);
        lcd.clear();
        lcd.setCursor(0, 0);
    	  lcd.print(F("Password"));
      }
    }
  }
  delay(50);

  
}

//----------------------------------------
//funzione inserimento password per attivazione/disattivazione stato di manutenzione
void passman(){
 
  key = keypad.getKey();//memorizza il tasto premuto sul keypad
  //ritorna nel menu principale se non si preme un tasto da piu del tempo inserito in durataIns o se si preme il tasto '#'
  if (millis() - tempIns >= durataIns || key == '#') {
    passmanc = false;
    lcd.clear();
    for(int i; i >= 5; i++){
      tempPassword[i] = "";
    }
    tempPasswordIndex = 0;
  }
  //se il tasto premuto non è '#'
  else if (key){
    noRetro = millis();//salva quando è stato premuto il tasto
    tempPassword[tempPasswordIndex] = key; //legge la password
    tempPasswordIndex++;//aumenta l'indice di 1
    lcd.setCursor(tempPasswordIndex - 1,1);//imposta il cursore sul display 
    lcd.print(F("*"));//stampa il carattere '*' sull'lcd
    tone(1,1200);
    delay(20);
    noTone(1);
    if(tempPasswordIndex>4) //controlla se l'indice password è maggiore di 4
    {
      tempPasswordIndex = 0;//resetta l'indice della password
      if(checkPassword()) //se la password è corretta
      {
        //indica all'utente che la password è corretta con una scritta sull'lcd
        lcd.clear();
        lcd.setCursor(4, 1);
  		  lcd.print(F("DifAlarm"));
        delay(500);
        lcd.setCursor(3,0);
        lcd.print(F("Password V"));
        delay(500);
        passmanc = false;//esce dal ciclo di inserimento della password
        //se l'allarme è inserito lo disinserisce
        if(ins){
          ins = false;
        }
        //se l'allarme è scattato a causa dei sensori anti-manomissione lo disattiva
        if (almMan){
          almMan = false;
        }
        man = !man;//inverte la variabile che determina lo stato di manutenzione
        lcd.clear();
        

      }
      else  //indica all'utente che la password è sbagliata con una scritta sull'lcd
      {
        lcd.clear();
        lcd.setCursor(4, 1);
  		  lcd.print(F("DifAlarm"));
        delay(500);
        lcd.setCursor(3,0);
        lcd.print(F("Password X"));
        delay(500);
        lcd.clear();
        lcd.setCursor(0, 0);
    	  lcd.print(F("Password"));
      }
    }
  }
  delay(50);

  
} 

 
//controlla se la password è corretta oppure no
boolean checkPassword(){ 

  if(strncmp(password, tempPassword, 4) == 0)
  {
    return true;
  }
  else {   
  return false;
  }
}

//controlla i pin collegati ai sensori 
void controllo(){
  lcd.setCursor(14, 0);
  //se il pin è su alto stampa il carattere 'v'
  if(digitalRead(13) == HIGH){
    lcd.print(F("V"));
  }
  //altrimenti se il pin è su basso stampa il carattere 'x'
  else if(digitalRead(13) == LOW){
    lcd.print(F("X"));
  }
  lcd.setCursor(15, 0);
  //se il pin è su alto stampa il carattere 'v'
  if(digitalRead(12) == HIGH){
    lcd.print(F("V"));
  }
  //altrimenti se il pin è su basso stampa il carattere 'x'
  else if(digitalRead(12) == LOW){
    lcd.print(F("X"));
  }
}

//gestisce la retro illuminazione
void retro(){
  //se l'allarme NON è scattato
  if (digitalRead(11) == LOW){
    //se sono passati 5sec dall'ultima pressione di un tasto spegne la retro illuminazione
    if (millis() - noRetro >= 5000) {
      lcd.noBacklight();
    }
  }
  //altrimenti la accende
  else{
    lcd.backlight();
  }
}

//controlla lo stato dell'allarme(inserito/manutenzione) e chiama le funzioni che controllano i sensori
void stato(){
  lcd.setCursor(4, 1);
  lcd.print(F("DifAlarm"));
  //se lo stato di manutenzione è attivo stampa "MAN" sull'lcd
  if (man){
    lcd.setCursor(13,0);
    lcd.print(F("MAN"));
  }
  //se lo stato di manutenzione NON è inserito
  else{
    scattoman();
  }
  //se l'allarme non è inserito
  if(ins == false && reset == false){
    //se l'allarme non è scattato a causa dei sensori anti-manomissione spegnilo
    if (almMan == false){
      digitalWrite(11,LOW);
    }
    //stampa "OFF" sull'lcd
    lcd.setCursor(0,0);
    lcd.print(F("OFF"));
  }
  //altrimenti(allarme inserito)
  else{
    //stampa "ON" sull'lcd
    lcd.setCursor(0,0);
    lcd.print(F("ON"));
    //chiama le funzioni che controllano i sensori
    scatto();
    scattopre();
  }
}

//controlla i sensori collegati al pin 13
void scatto(){
  //appena il circuito si apre stampa "ALM" sull'lcd e accende la sirena (pin 11)
  if(digitalRead(13) == LOW){
    lcd.setCursor(13,0);
    lcd.print(F("ALM"));
    digitalWrite(11,HIGH);
  }
  delay(20);//piccola pausa
}

//controlla i sensori collegati al pin 12
void scattopre(){
  //legge lo stato del pin 12 solo se questo è falso
  if (condizione == false){
  	condizione = digitalRead(12) == LOW;//se true indica che il circuito è stato aperto
    canSound = true;//abilita il buzzer
  }

  //se il il circuito è stato aperto
  if (condizione) {
    //se il buzzer può suonare emette dei bip per ricordare all'utente di disinserire l'allarme prima che esso scatti
    if (canSound){
      tone(1, 1200);
      delay(50);
      noTone(1);
      delay(50);
  	}
    reset = true;//permette di resettare il timer del pre-allarme

    if (startTime == 0) {
      startTime = millis(); // Memorizza il tempo di inizio
    }
      //se è passato più tempo del valore di durata
      if (millis() - startTime >= durata) {
        //stampa "ALM" sull'lcd
        lcd.setCursor(13,0);
    	  lcd.print(F("ALM"));
        //accende la sirena
        digitalWrite(11,HIGH);
        //impedisce al buzzer di suonare
        canSound = false;
        noTone(1);
      }
  }
}
//controlla i sensori anti-manomissione
void scattoman(){
  //se il circuito viene aperto
  if(digitalRead(10) == LOW){
    //stampa "ALM" sull'lcd
    lcd.setCursor(13,0);
    lcd.print(F("ALM"));
    almMan = true;//indica che l'allarme è scattato a causa dei sensori anti-manomissione
    digitalWrite(11,HIGH);//accende la sirena
  }
  delay(20);//piccola pausa
}

void setup() {
  pinMode(13,INPUT);//sensori finestre (scatto dell'allarme istantaneo), l'allarme può essere disattivato con il tasto '#' e inserimento password
  pinMode(12,INPUT);//senore porta di ingresso (scatto dell'allarme dopo i secondi contenuti nella variabile durata), l'allarme può essere disattivato con il tasto '#' e inserimento password
  pinMode(10,INPUT);//sensori anti-manomissione (scatto dell'allarme istantaneo), l'allarme può essere disattivato con il tasto '*' e inserimento password
  pinMode(11,OUTPUT);//rele sirena
  pinMode(1,OUTPUT);//buzzer
  pinMode(0,OUTPUT);//pin collegato con il reset


  lcd.init(); //inizializza l'lcd
  lcd.backlight(); //accende la retroilluminazione
  lcd.setCursor(4, 1);
  lcd.print(F("DifAlarm"));
  delay (1000);
  lcd.setCursor(0, 0);
  lcd.print(F("Inizializzazione"));
  delay (1500);
  lcd.clear();
  noRetro = millis();

}



void loop(){
  stato();//chiama la funzione stato per aggiornare l'utente sullo stato dell'allarme
  retro();//chiama la funzione retro per spegnere/accendere la etroilluminazione
  char key = keypad.getKey();//salva il carattere premuto sul keypad
  //se viene premuto un tasto
  if(key){
    noRetro = millis();//salva quando viene premuto il tasto(gestione retro illuminazione)
    tempIns = millis();//salva quando viene premuto il tasto(ritorno al menu principale automatico)
    lcd.backlight();
  }
  //se il tasto premuto è '#',l'allarme non èinserito,lo stato di manutenzione non è attivo e l'allarme non è scattato a causa dei sensori anti-manomissione(quest'ultima cosa serve per non far inserire l'allarme se questo è stato manomesso)
  if (key == '#' && ins == false && man == false && almMan == false){
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print(F("Password"));
    passc = true;//accetta l'inserimento della password
    home = true;
    //finche la password non è corretta
    while(passc){
      //se lo stato di manutenzione è disattivato chiama la funzione che controlla i sensori anti-manomissione
      if (man == false){
        scattoman();
      }
    	pass();//funzione inserimento password
      controllo();//controlla se porta e finestre (pin 12 e 13) sono chiuse
      retro();//gestione retroilluminazione
    }
    //se l'arduino ritorna in questo if dopo che la password è corretta inserisce l'allarme
    if (home){
      lcd.clear();
      lcd.setCursor(1,0);
      startTime = millis();
      lcd.print(F("Inserimento..."));
      delay(15000);//tempo per uscire di casa senza far scattare l'allarme
      noRetro = millis();
      startTime = millis();
      ins = true;//allarme inserito
      lcd.clear();
    }
    //se l'arduino ritorna in questo if perche sono passati 10sec dall'ultima pressione di un tasto o si preme il tasto '#' nella funzione pass()
    else{lcd.clear();}
  }
  
  //se il tasto premuto è '#',l'allarme è inserito e lo stato di manutenzione è spento
  else if (key == '#' && ins == true && man == false){
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print(F("Password"));
    passc = true;//accetta l'inserimento della password
    home = true;
    //finche la password non è corretta
    while(passc){
      //se lo stato di manutenzione è disattivato chiama la funzione che controlla i sensori anti-manomissione
      if (man == false){
        scattoman();
      }
    	pass();//funzione inserimento password
      retro();//gestione retroilluminazione
      //impedisce al buzzer di suonare
      canSound = false;
      noTone(1);
      //controlla i sensori
      scatto();
      scattopre();
    }
    //se l'arduino ritorna in questo if dopo che la password è corretta disinserisce l'allarme
    if (home){
      ins = false;
    }
  }

  //se il tasto premuto è '*'
  if (key == '*'){
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print(F("Password"));
    passmanc = true;//accetta l'inserimento della password
    //finche la password non è corretta
    while(passmanc){
      passman();//funzione inserimento password manutenzione
      retro();//gestione retro illuminazione
      //se l'allarme è inserito controlla i sensori 
      if (ins){
        scatto();
        scattopre();
      }
      //se lo stato di manutenzione è disattivato chiama la funzione che controlla i sensori anti-manomissione
      if (man == false){
        scattoman();
      }
    }
  }

  //se il tasto premuto è 'd'
  if(key == 'd'){
    hardReset = true;//imposta la variabile per il reset su true
    passc = true;//accetta l'inserimento della password
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print(F("Password"));
    //finchè la password non è corretta
    while(passc){
      pass();//funzione inserimento password
      retro();//gestione retro illuminazione
      //se lo stato di manutenzione è disattivato chiama la funzione che controlla i sensori anti-manomissione
      if (man == false){
        scattoman();
      }
    }
  }
  //resetta il timer se il cicuito sul pin 12 si chiude e l'allarme viene disinserito tremite password diventa falsa
  if (digitalRead(12) == HIGH && reset == false) {
   	startTime = millis();
  }

}

Per Wokwi ottimo, ma invece di farne un link nel testo puoi postarlo anche direttamente nel post, così il forum ne fa anche una anteprima, viene così:

Fai attenzione con gli array e c string.
La funzione void pass() contiene queste righe:

//resetta la password
    for(int i; i >= 5; i++){
      tempPassword[i] = "";
    }
    tempPasswordIndex = 0;

Il for non viene eseguito neanche una volta e per fortuna, perché se venisse eseguito tempPassword finirebbe per contenere cinque d.
Il codice corretto:

for(int i=0; i < 5; i++) {
      Serial.println(i);
      tempPassword[i] = '\0';
  }

Però di consiglio di usare la funzione standard della librerie C memset(), così:

memset(tempPassword, '\0', 5);

Ciao.

Gisuto, ma non necessario. Per "resettare" una stringa basta mettere a zero il primo carattere, ed in questo caso poi dato che il "puntatore" alla posizione dove scrivere è "tempPasswordIndex", si azzera anche quello:

  if (millis() - tempIns >= durataIns || key == '#') {
    passmanc = false;
    lcd.clear();
    // Resetta il buffer
    tempPassword[0] = 0;
    tempPasswordIndex = 0;
  }

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.