Aiuto gioco codice morse

salve a tutti..
chiedevo un aiuto per creare un semplice giochino da far fare a nipotini e amici quando creo in garage piccole Escape room.

La parte hardware è molto semplice :
1 pulsante
8 led
1 relè
1 Arduino uno

Praticamente volevo far si che l'utente seguendo un codice morse su un foglio sbloccasse una serratura. 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.

esempio di codice:

. . _ . _ . _ .

quindi (.) 1 click mentre (_) pressione di 1 secondo

Grazie in anticipo
a presto :wink: :wink: :wink: :wink:

Buongiorno,
essendo il tuo primo post, nel rispetto del regolamento della sezione Italiana del forum (… punto 13, primo capoverso), ti chiedo cortesemente di presentarti IN QUESTO THREAD (spiegando bene quali conoscenze hai di elettronica e di programmazione ... possibilmente evitando di scrivere solo una riga di saluto) e di leggere con molta attenzione tutto il su citato REGOLAMENTO ... Grazie. :slight_smile:

Guglielmo

P.S.: Ti ricordo che, purtroppo, fino a quando non sarà fatta la presentazione nell’apposito thread, nessuno ti potrà rispondere, quindi ti consiglio di farla al più presto. :wink:

Feffone:
chiedevo un aiuto per creare un semplice giochino da far fare a nipotini e amici quando creo in garage piccole Escape room.

Ok, qual è la domanda quindi?
Come specificato nel regolamento, qui trovi aiuto a correggere, ampliare o migliorare TUOI progetti, ma non realizziamo codice "per conto terzi". Il che significa che prima devi iniziare a scrivere TU qualcosa e poi, in caso di problemi o dubbi, possiamo aiutarti.

Il consiglio, se hai ancora poca dimestichezza con l'ambiente e/o col linguaggio, di fare prima alcune prove cercando di capire come gestire ed implementare SINGOLI elementi. Quindi nel tuo caso ti consiglierei di iniziare collegando ad Arduino un pulsante (collegandolo da un lato ad un pin e dall'altro a GND, ed una resistenza da 10k dal pin verso +5V) e due LED (ognuno ad un altro pin, con in serie una resistenza da 220 Ohm), ed iniziare a cercare di capire come rilevare la pressione di un punto (.) o di una linea (_), il che si riduce a contare per quanto tempo resta premuto il pulsante ed accendere il led 1 se rilevi un punto o il 2 se è una linea.

Appena avrai questo codice, se non ti funziona o hai difficoltà posta qui il codice, unitamente a cosa fa di sbagliato e ti aiutiamo.

Se invece il codice riesci a farlo andare, è il "nucleo" di quello che vorresti fare per cui puoi iniziare ad estenderlo con un terzo LED da accendere solo quando viene rilevata la sequenza speciale, ed alla fine sostituire il terzo LED con il relè.

Chiedo scusa hai ragione!!!

il mio problema è dare il tempo di pressione prima di far fare il secondo comando
ho provato con le attese ma come se non fossero rispettate.
grazie a tutti

const int pulsante = 2;     
const int led1 =  13;      
const int led2 =  12;  
const int led3 =  11;  
int prespulsante = 0;       

void setup() {

  pinMode(led1, OUTPUT);
  pinMode(led2, OUTPUT);
  pinMode(led3, OUTPUT);
  pinMode(pulsante, INPUT);
}

void loop() {
  prespulsante = digitalRead(pulsante);

  if (prespulsante == HIGH) {
   
    digitalWrite(led1, HIGH);
    delay(2000);
     }
    if (prespulsante == HIGH) {
  
    digitalWrite(led2, HIGH);
  } else {
   delay(2000);
    digitalWrite(led1, LOW);
    digitalWrite(led2, LOW);
  }
}

Togli tutti i delay()! Il delay blocca il flusso del programma, perciò va usato solo quando è lecito bloccare il flusso, ad esempio nelle impostazioni all'accensione nel setup.
Per queste cose devi rilevare il tempo con millis(): nel momento in cui il pulsante viene premuto (e prima non lo era) memorizzi millis() in una variabile; se viene lasciato entro la durata massima del punto, è un punto, altrimenti è una linea.
Metti sempre un condensatore da 100~220nF in parallelo al pulsante per evitare i rimbalzi, cioè letture erroneamente multiple dovute a falsi contatti.

Puoi provare a cercare su Google "arduino button library". Ci sono anche molti esempi.

Ciao,
P.

esempio di codice:

. . _ . _ . _ .

Ah... le mie iniziali :smiley:

Tutto giusto il discorso sul loop non bloccante... ma questo mi sembra un compito in cui bloccarlo non ha importanza, e usare pulseIn per leggere il tempo di pressione del pulsante mi sembra la soluzione più semplice.

L'appetito vien mangiando... Che facciamo se poi vuole fare anche un'altra cosa, ad esempio un led che lampeggia?... :slight_smile:

Grazie Claudio
Ceno e poi provo la formula pulseIn.
Grazie a tutti per l’interessamento e per i vostri consigli.

Feffone:
il mio problema è dare il tempo di pressione prima di far fare il secondo comando
ho provato con le attese ma come se non fossero rispettate.

Non "partire in quarta", te l'ho già detto qual è il mio consiglio... Devi fare un passo per volta ed imparare gradualmente come gestire quello che ti occorre, e soprattutto ricorda sempre di specificare come hai collegato pulsanti e led.

Te lo ripeto:

  1. inizia collegando ad Arduino un pulsante e due LED ed iniziare a cercare di capire come rilevare la pressione di un punto (.) o di una linea (_), il che si riduce a contare per quanto tempo resta premuto il pulsante ed accendere il led 1 se rilevi un punto o il 2 se è una linea.
    Come già ti hanno detto, non devi usare delay() ma memorizzare prima il valore di millis() quando si preme il pulsante, e quando viene rilasciato calcoli la differenza tra il millis() attuale ed il precedente, e se è inferiore al valore che avrai impostato come durata massima del punto sarà un punto, se superiore sarà una linea (direi che un secondo per la linea è pure troppo, bastano 500 o 600 ms...).
    Con questa prima fase inizi a prendere pratica con millis() e la gestione di eventi a tempo.

  2. una volta che avrai fatto questo (e non cercare di anticipare facendo tutto insieme da subito questa cosa!!), puoi iniziare ad estenderlo con un terzo LED da accendere solo quando viene rilevata la sequenza "speciale" predefinita. Per farlo devi per prima cosa accumulare in una variabile i valori man mano letti dl pulsante, ad esempio in un array di char (una "stringa C"), dove aggiungi il carattere '.' quando rilevi un punto, e '-' quando è una linea, e confronti questi caratteri con la stringa che rappresenta il "codice segreto". L'array dei caratteri premuti avrà la stessa dimensione di quello del codice segreto (nel tuo caso 8 caratteri, quindi devi dimensionare a 9 elementi).
    Con questa seconda fase impari come si gesticono le "stringhe C".

  3. ed alla fine (ma solo alla fine) quando funziona tutto il precedente, ti basterà sostituire il terzo LED con il relè, ed aggiungere eventualmente altre cose come un display LCD per mostrare dei messaggi, e/o la sequenza di caratteri digitata....

Non farti quindi prendere dalla "fretta" (a Roma si dice "nun fa' de prescia"...), fai prima il primo programmino e poi quando funziona passa al secondo, e così via!

eccomi qua, dopo vari tentativi e ricerche di formule per me sconosciute ….sono riuscito per metà nel mio intento…

vi posto i primi risultati … ma aimè manca ancora parecchio!!
spero di essere sulla strada giusta …

int conteggio;
int ciclo;
const int ON = HIGH;
const int OFF = LOW;
long t0;
long t;
long t1;
int LED1 (13);
int LED2 (12);


void setup() {
  Serial.begin(9600);
  pinMode ( 2 , INPUT);
  pinMode (13, OUTPUT);
  pinMode (12, OUTPUT);
  conteggio = OFF;
  ciclo = 0;
}

void loop() {
  int tasto = digitalRead (2);
  if ((tasto == HIGH) && (conteggio == OFF)) {
    conteggio = ON;
    t0 = millis();

  }
  if ((tasto == LOW) && (conteggio == ON)) {
    conteggio = OFF;
    t = millis() - t0;
    Serial.println("codice: LUNGO,CORTO,CORTO,LUNGO,CORTO,LUNGO,CORTO,LUNGO");
    Serial.println("_ . . _ . _ . _");

    Serial.println("tempo = ");
    Serial.println(t);        //PER CONTROLLO TEMPO DI PRESSIONE


    if (t > 500 && ciclo == 0) {                 //TEMPO PRESSIONE LUNGO
      digitalWrite (LED1, HIGH);       //  ACCENDE PRIMO LED
      digitalWrite (LED2, LOW);
      ciclo ++;

    }
   


    if
    (t < 500 && digitalRead(LED1) == HIGH && ciclo == 1)   {    //ACCENDO SECONDO LED
      digitalWrite (LED2 , HIGH);
      ciclo ++;
    }


  }




}

Il problema ora sta nel dirgli che se sbaglio le pressioni si resetti il tutto…

grazie a tutti :slight_smile: :slight_smile:

>Feffone: il codice va racchiuso nei tag CODE e NON nei tag "quote" ! Ho corretto io il tuo post qui sopra, ma cortesemente, presta più attenzione. Grazie :slight_smile:

Guglielmo

La variabile conteggio andrebbe chiamata tasto_prec, perché memorizza la condizione del tasto.

Allora, qualche nota:

  • La variabile “conteggio” in realtà non “conta” nulla, e vale solamente ON o OFF: quindi è meglio definirla “bool” così vale dorettamente solo “true” o “false” ed eviti i vari ON, OFF, HIGH, LOW…
  • Ove possibile usa nomi di variabili che descrivano (brevemente…) il loro scopo, ad esempio “t” potresti chiamarla “TempoPressione”, e la “conteggio” in
  • I valori iniziali delle variabili globali (es. “ciclo”) puoi metterli direttamente nella definizione (“int ciclo = 0 ;”), inutile doversi ricordare di metterli nel setup()
  • Le variabili che devono contenere valori di millis() devono avere lo stesso tipo restituito da millis() quindi non “long” ma “unsigned long”
  • Le definizioni che hai fatto di LED1 e LED2 sono sbagliate, in quel modo tu stai definendo degli array di valori interi! Per fare quello che pensavi devi usare le #define
  • Le variabili non utilizzate, come “t1”, cancellale perché altrimenti occupi inutilmente preziosa RAM
  • in questa prima fase, lascia stare la sequenza e la variabile “ciclo”, evita di “andare di fretta”, ci penserai nella seconda fase quando questo programma accenderà un led per il punto e l’altro per la linea.
  • Se possibile, per migliorare la leggibilità del codice evita di mettere più di una riga vuota consecutiva (mettine UNA solo per separare blocchi eventualmente “importanti” o diversi tra loro), metti a capo anche la fraffa di apertura di un blocco per evidenziare meglio inizio e fine blocco

In quest’ottica, guarda come ti ho modificato il tuo listato (non l’ho provato, sta a te provarlo e vedere se funziona o meno…):

// Configurazione:
#define LED1 13
#define LED2 12
#define TASTO 2
// Millisecondi di durata massima del punto
#define MAX_PUNTO 300

int tasto_prec = LOW;
unsigned long t0;
long tempoOn = 0;

void setup() 
{
  Serial.begin(9600);
  pinMode (TASTO, INPUT);
  pinMode (LED1, OUTPUT);
  pinMode (LED2, OUTPUT);
}

void loop() 
{
  int tasto = digitalRead(TASTO);
  if ( tasto == HIGH && tasto_prec == LOW ) 
  { // Se è la prima volta che premo il tasto
    t0 = millis();
    delay(50); // Un minimo di debounce (meglio se lo fai hardware)
  }
  else 
  { // Se il tasto è stato rilasciato ma in precedenza lo avevo premuto
    if ( tasto == LOW && tasto_prec == HIGH ) 
    {
      // Calcolo il tempo totale
      tempoOn = millis() - t0;
      // --- Per debug:
      Serial.println("tempo = ");
      Serial.println(t);
      // ---------------------------------

      if (tempoOn > MAX_PUNTO) 
      { // LINEA: accende il primo LED
        digitalWrite (LED1, HIGH);
        digitalWrite (LED2, LOW);
      }
      if (tempoOn <= MAX_PUNTO)
      { // PUNTO: accende il secondo LED
        digitalWrite (LED1, LOW);
        digitalWrite (LED2, HIGH);
      }
    }
  }
  tasto_prec = tasto;
}

tasto e tasto_prec possono essere byte, risparmiando 2 byte (anche bool, ma occupa comunque un byte).

Si, lo so, Gianluca, ma in questa fase sicuramente non è rilevante risparmiare un paio di byte… :wink:

Intanto vediamo se riesce a fare quella prova col codice che gli ho postato, e quindi se riesce a capire come si rileva la durata della pressione del tasto, e da qui trasformare la logica in una funzioncina, che restituisce cosa rileva, che poi accumula in una stringa… Eccetera eccetera. Ma una cosa per volta.

PS: potremmo pure usare gli interrupt, ma ho preferito evitare di confondergli ulteriormente le idee… 8)

buona sera a tutti …
ho caricato il codice su Arduino ma niente ,prima mi da un errore di poco conto che è il Serial.println(t); risolto, poi però non funziona, almeno non fa quello che intendo io , cioè ogni volta che azzecchi un codice corretto accende un led ( volevo mettere 8 led e un relè. ). ogni volta che ne sbaglio uno si azzera e riparte fino al corretto inserimento della sequenza del codice che farà scattare con un relè la serratura di una scatola.

grazie ancora a tutti
:slight_smile: :slight_smile: :slight_smile:

Feffone:
poi però non funziona, almeno non fa quello che intendo io , cioè ogni volta che azzecchi un codice corretto accende un led ( volevo mettere 8 led e un relè. ). ogni volta che ne sbaglio uno si azzera e riparte fino al corretto inserimento della sequenza del codice che farà scattare con un relè la serratura di una scatola.

Ancora?... Scusa, ma ti avevo consigliato di andare per passi, quando quel primo listato che ti ho modificato funziona, passi al secondo passo dove accumuli in un array i segni man mano ricevuti, e poi quando quello funziona completare il codice aggiungendo il riconoscimento della sequenza corretta.

Ora decidi, o segui i consigli che ti do' (e tra l'altro dici che "non funziona" ma non hai neanche postato il tuo codice che "non funziona", quindi che aiuto possiamo darti secondo te?...), oppure se vuoi fare di testa tua, scusa ma allora io passo, finisciti da solo sto codice.

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... :slight_smile: 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à:

Grazie mille Claudio ma sono riuscito già nel mio intento e in più ho inserito un display 2x16 che codifica la lettera premuta e con la parola scatta la serratura... se a qualcuno interessasse basta chiedere che ve lo pubblico.
Gazie a tutti a presto