Feffone:
Assegnando un impulso a ogni (.) e un pressione di 1 secondo ogni (_) cosi facendo ad ogni pressione corretta accenda un led e ogni pressione sbagliata faccia si che si spenga tutto per ricominciare poi da capo,una volta indovinato la sequenza scatti la serratura.
Invece di cercare di comporre istruzioni "a martellate" cercando di fargli ottenere prima o poi il risultato sperato, è molto meglio prendersi un po' di tempo e RIdescrivere la procedura a parole, ma usando la logica comprensibile alla macchina (sequenza di comandi e condizioni). Questo passo è fondamentale per trasformare qualsiasi idea in una procedura eseguibile in modo automatico e andrebbe fatto sempre prima di scrivere una sola riga di codice. Quello che hai descritto in italiano corrente lo puoi esprimere ugualmente con la seguente procedura in italiano macchinese, cioè in pseudocodice:
LEGGI SIMBOLO
SE CORRETTO:
AVANTI SEQUENZA
AGGIORNA LED
SE SEQUENZA COMPLETATA:
ATTIVA SERRATURA
AZZERA
AGGIORNA LED
ALTRIMENTI:
AZZERA
AGGIORNA LED
Se questa procedura funziona, nel senso che la logica su carta fa quello che vuoi, la traduzione è immediata e funzionerà esattamente come progettato:
void loop()
{
leggiSimbolo();
if (simboloCorretto())
{
avantiSequenza();
aggiornaLED();
if (sequenzaCompletata())
{
attivaSerratura();
azzera();
aggiornaLED();
}
}
else
{
azzera();
aggiornaLED();
}
}
È un programma in C scritto in italiano...
Rimangono solo i "dettagli implementativi", cioè quali sono e come vanno manipolati i dati di lavoro, e anche questi li decidiamo noi prima su carta.
Essendoci un codice da indovinare in vari passaggi, la cosa più semplice mi sembra avere il codice in memoria in una stringa di simboli (array di char), assieme ad una variabile che ci indica a che punto siamo arrivati:
char codice[] = "..-.-.-.";
byte avanzamento = 0;
In questo modo abbiamo una procedura ben chiara da seguire (non una descrizione discorsiva potenzialmente ambigua), e i dati con cui lavorare (che abbiamo deciso noi). Rimangono solo da dettagliare tutte quelle funzioncine dal nome e scopo chiari e univoici.
Ad esempio come faccio a sapere se un simbolo letto è corretto? Stabilisco che la funzione 'leggiSimbolo' aggiorni una ulteriore variabile char (magari per chiarezza chiamata 'simbolo') che potrà valere '.' o '-', a quel punto sapere se quel simbolo è giusto nell'attuale stato di avanzamento è immediato:
bool simboloCorretto()
{
if (simbolo == codice[avanzamento])
{
return true;
}
else
{
return false;
}
}
E avanti sequenza cosa deve fare? Semplicemente aggiornare lo stato di avanzamento:
void avantiSequenza() { avanzamento++; }
E sequenza completata? Semplicemente vedere se lo stato di avanzamento ha raggiunto la lunghezza del codice:
bool sequenzaCompletata()
{
if (avanzamento == strlen(codice))
{
return true;
}
else
{
return false;
}
}
Aggiorna LED userà il valore di 'avanzamento' per accendere i LED desiderati, 'attivaSerratura' comanderà il relé di apertura per un certo tempo, 'azzera' può semplicemente riportare a zero il valore di 'avanzamento', eccetera eccetera. Visto che sono alcuni anni che aggeggi con Arduino queste cose e l'uso delle funzioni non dovrebbero essere troppo oscure. L'importante è schematizzare la logica, cosa va risolto dove, a blocchetti elementari e non tutto quanto assieme.
Ripeto che secondo me, viste le difficoltà, parlare di loop non bloccante e ottimizzazioni varie fa solo confusione, e che la 'pulseIn' (spiegata chiaramente nel reference) mi sembra il modo più semplice per acquisire la durata della pressione di un singolo pulsante (con debounce hardware) in un sistema che nel frattempo non deve fare niente altro.
Poi le alternative percorribili sono moltissime, di varia compattezza, compresa quella in stile ladder PLC che permette di riconoscere linee/punti in modo non bloccante con solo quattro righe di codice... giusto per curiosità: