Pianola con Arduino

dab77:
Francesco, potrebbe essere l'occasione per imparare una cosa nuova! registri le 7 note come sette bit di un byte, e controlli i singoli valori dei byte. non cambia niente come risultato, ma utilizzi un metodo molto più elegante. io ci proverei.

Un passo alla volta. Per ora non gli serve questo, gli serve come imparare a gestire i pulsanti :smiley:
Anche perché ha già dei byte che contengono i livelli logici dei pin accessibili a singoli bit, sono i registri del microcontrollore. Le porte sono infatti mappate in alcuni registri e leggere un registro equivale a leggere un gruppo di 8 porte tutte insieme.
http://www.arduino.cc/en/Reference/PortManipulation
Ma qui si entra in un livello più elevato di programmazione, mentre ora deve imparare a programmare correttamente.

francescobonassi:
Allora... Ho sostituito gli = con == e adesso suona :% Però la nota non torna mai bassa. Se premo il Do questo suona finchè non premo un'altra nota che a sua volta farà lo stesso. Ho prova a mettere dopo ogni nota:

else

{
   noTone(Buzzer);
}



...ma peggio che prima. Ho ottenuto delle frequenze altissime senza spiegarmi il perchè.

Forse perché si sommano più frequenze insieme. Oppure perché fai un'alternanza superveloce fra nota/no nota....
Un altro problema che nessuno ha sollevato è il debounce dei pulsanti. Ogni pulsante dà dei "rimbalzi" elettrici, che possono essere letti come false pressioni. Anche questo tema è da sviluppare.

Poi secondo me dovresti provare un altro approccio. Semplifica il programma e fai suonare una singola nota per un determinato lasso di tempo, metti cioè un flag per verificare che non sia già in esecuzione la riproduzione di una nota.
Poi il secondo passo potrebbe essere quello di far suonare una nota finché il tasto è premuto.

leo72:

francescobonassi:
Allora... Ho sostituito gli = con == e adesso suona :% Però la nota non torna mai bassa. Se premo il Do questo suona finchè non premo un'altra nota che a sua volta farà lo stesso. Ho prova a mettere dopo ogni nota:

else

{
   noTone(Buzzer);
}



...ma peggio che prima. Ho ottenuto delle frequenze altissime senza spiegarmi il perchè.

Forse perché si sommano più frequenze insieme. Oppure perché fai un'alternanza superveloce fra nota/no nota....
Un altro problema che nessuno ha sollevato è il debounce dei pulsanti. Ogni pulsante dà dei "rimbalzi" elettrici, che possono essere letti come false pressioni. Anche questo tema è da sviluppare.

Poi secondo me dovresti provare un altro approccio. Semplifica il programma e fai suonare una singola nota per un determinato lasso di tempo, metti cioè un flag per verificare che non sia già in esecuzione la riproduzione di una nota.
Poi il secondo passo potrebbe essere quello di far suonare una nota finché il tasto è premuto.

Si, si hai perfettamente ragione, lo suggerivo solo per dopo, cioè quando hai fatto funzionare tutto e vuoi migliorare.
Secondo me però aveva colto il punto col 'while', e non capisco perchè non si può usare. in fondo finchè è 'Vero' deve suonare, poi manca la condizione di uscita, ovvero quando il pin è falso. Non è così?

Esistono 2 modi per intendere il "finché è vero deve suonare":

  1. finché non stacca il pulsante, deve suonare
  2. finché non viene premuto un altro tasto, deve suonare

Nel 1° caso può usare il while (e va messo appunto il controllo del cambio di stato del pin per l'uscita), nel secondo caso, non può usare il while ma deve usare un ciclo per cambiare nota solo quando viene premuto un altro pulsante.

francescobonassi:
Allora... Ho sostituito gli = con == e adesso suona :% Però la nota non torna mai bassa. Se premo il Do questo suona finchè non premo un'altra nota che a sua volta farà lo stesso. Ho prova a mettere dopo ogni nota:

else

{
  noTone(Buzzer);
}



...ma peggio che prima. Ho ottenuto delle frequenze altissime senza spiegarmi il perchè.

hai provato a mettere una roba del genere?

if(digitalRead(Do) == HIGH){
 tone(Buzer, 262, duration); // suona il DO per duration ms (duration lo dichiari come const int )
 delay(duration); //attende che il do abbia finito di suonare
 noTone(Buzer); //smette di far suonare il Buzer
}
[\code]

x IngHooch
Se ho capito bene però "duration" ha valore fisso e la nota rimane attiva per un lasso di tempo predefinito, nel caso volessi prolungare la nota tenendo premuto il pulsante non posso farlo...

x leo72
Da quello che dici capisco che è più corretto usare i while... Usando gli if ottengo appunto quello che ho ottenuto; ogni nota suona finchè non ne premo un'altra.

francescobonassi:
x IngHooch
Se ho capito bene però "duration" ha valore fisso e la nota rimane attiva per un lasso di tempo predefinito, nel caso volessi prolungare la nota tenendo premuto il pulsante non posso farlo...

Nì, perché se il codice è fatto così

 void loop(){

if(digitalRead(Do) == HIGH){
 tone(Buzer, 262, duration); // suona il DO per duration ms (duration lo dichiari come const int )
 delay(duration); //attende che il do abbia finito di suonare
 noTone(Buzer); //smette di far suonare il Buzer
}

}

Se tu tiene premuto il bottone ogni volete che passa dall'if trova i bottone premuto e continua a suonare la stessa nota, nel momento in cui lo lasci questo continua a suonare per duration e poi smette

mmm... qualcosa non mi torna... tutto quello che dici però accade se il tasto è premuto. Supponiamo che imposto il delay a 10ms... Appena premo il tasto suona il do per 20ms (10ms per la funzione tone + 10ms per il delay successivo) e poi si spegne... Indipendentemente che io rilascio o meno il pulsante... Corretto?

Funziona facendo così!!!

if(ValueDo==HIGH)
{   tone(Buzzer,262);
delay(10);
noTone(Buzzer);
}

IngHooch come dici tu però senza impostare il tempo alla funzione tone.

Però ora si tratta solo di trovare il delay giusto perchè a 10 è troppo basso e distorce... Però a 100 si sente il buzzer staccare e attaccare velocemente.

No l'istruzione

if(digitalRead(Do) == HIGH){
tone(Buzer, 262, duration);
delay(duration);
noTone(Buzer);
}

if(digitalRead(La) == HIGH){
//codice LA
}

da il via al Buzer per duration, ma non aspetta che finisce! Passa subito all'istruzione successiva, che nel nostro caso è un delay di duration.
Quindi le due righe di codice forzano il programma ad aspettare che il do abbia finito di suonare prima di procedere ad altri controlli (nel nostro caso controllare se il La è premuto ed eventualmente farlo suonare)

francescobonassi:
Funziona facendo così!!!

if(ValueDo==HIGH)

{   tone(Buzzer,262);
delay(10);
noTone(Buzzer);
}




IngHooch come dici tu però senza impostare il tempo alla funzione tone.

Però ora si tratta solo di trovare il delay giusto perchè a 10 è troppo basso e distorce... Però a 100 si sente il buzzer staccare e attaccare velocemente.

Qui devi fare un po' di prove. Leggi il post precedente e guarda di capire bene la logica del duration + delay :%

Non riesco a capire...
if(digitalRead(Do) == HIGH){
tone(Buzer, 262, duration);
delay(duration);
noTone(Buzer);
}
Inizia a suona Do per duration, aspetta duration e poi spegne... Poi ricomincia... Non capisco l'utilità di impostare duration anche a tone.

Scusa hai ragione sono del tutto equivalenti le due istruzioni

tone(Buzer, 262, duration);

e

tone(Buzer, 262);
delay(duration);
noTone();

Tuttavia come ho già detto se usi la prima istruzione è buona norma mettere un delay di duration dietro in modo che non ti suoni due note insieme.

Per esempio:

tone(Buzer, 262, duration); //suona le due note insieme per duration
tone(Buzer, 565, duration);

se invece fai

tone(Buzer, 262, duration); //suona il DO
delay(duration); //aspetta che finisce il Do
tone(Buzer, 565, duration); //Suona un'altra nota

io pero' ritornerei ai while, l'unica cosa che devi fare e' che all'interno del while ci deve essere anche il controllo per vedere se il tasto e' ancora premuto o no.
Perche' quando entri nel while sei in un loop chiuso, fai solo quello che c'e' nel while, non altro.
Il loop stesso di arduino e' un while (l'ho imparato da leo :))

Testato:
io pero' ritornerei ai while, l'unica cosa che devi fare e' che all'interno del while ci deve essere anche il controllo per vedere se il tasto e' ancora premuto o no.
Perche' quando entri nel while sei in un loop chiuso, fai solo quello che c'e' nel while, non altro.
Il loop stesso di arduino e' un while (l'ho imparato da leo :))

E' un ciclo for. Cmq è un ciclo infinito da cui non se ne esce. E' un "raggiro" fatto per imbrogliare il compilatore perché in un programma in C che gira sul computer esiste il caso che, al termine del programma, il controllo sia reso al sistema operativo. Ma su un micro non esiste tale ambiente superiore, una "cosa" cioè a cui rendere il controllo, per cui nel core dell'Arduino all'interno del file main.c è stato inserito lo spezzone di codice contenuto in loop() all'interno di un ciclo infinito.

@Francesco:
esistono infinite combinazioni di casi che portano ad uno stesso risultato, ti sono state suggerite molte di esse. A questo punto prendi ed inizia a studiarti le varie casistiche altrimenti passi più tempo qui sul forum a chiedere questo o quello piuttosto che ad imparare a programmare per conto tuo :wink:

il loop di arduino e' un for ?
ci avrei giurato che tu mi avessi detto fosse un while,
cmq va bene cosi' :slight_smile:

Comunque sia... Il codice finale che ho è questo:

int Buzzer=13; //Collego il Buzzer al pin D13
int Do=12; //Collego il pulsante Do al pin D12
int Re=11; //Collego il pulsante Re al pin D11
int Mi=10; //Collego il pulsante Mi al pin D10
int Fa=9; //Collego il pulsante Fa al pin D9
int Sol=8; //Collego il pulsante Sol al pin D8
int La=7; //Collego il pulsante La al pin D7
int Si=6; //Collego il pulsante Si al pin D6

int ValueDo; //Valore del pulsante Do
int ValueRe; //Valore del pulsante Re
int ValueMi; //Valore del pulsante Mi
int ValueFa; //Valore del pulsante Fa
int ValueSol; //Valore del pulsante Sol
int ValueLa; //Valore del pulsante La
int ValueSi; //Valore del pulsante Si

void setup() //Azioni da eseguire solo all'avvio
{
  pinMode(Buzzer,OUTPUT); //Imposto in uscita il pin D13
  pinMode(Do,INPUT); //Imposto in ingresso il pin D12
  pinMode(Re,INPUT); //Imposto in ingresso il pin D11
  pinMode(Mi,INPUT); //Imposto in ingresso il pin D10
  pinMode(Fa,INPUT); //Imposto in ingresso il pin D9
  pinMode(Sol,INPUT); //Imposto in ingresso il pin D8
  pinMode(La,INPUT); //Imposto in ingresso il pin D7
  pinMode(Si,INPUT); //Imposto in ingresso il pin D6
}

void loop() //Azioni da ripetere all'infinito
{
  ValueDo=digitalRead(Do); //Associo al valore del pulsante Do la lettura al pin D12
  delayMicroseconds(10); //Aspetta 10us
  ValueRe=digitalRead(Re); //Associo al valore del pulsante Re la lettura al pin D11
  delayMicroseconds(10); //Aspetta 10us
  ValueMi=digitalRead(Mi); //Associo al valore del pulsante Mi la lettura al pin D10
  delayMicroseconds(10); //Aspetta 10us
  ValueFa=digitalRead(Fa); //Associo al valore del pulsante Fa la lettura al pin D9
  delayMicroseconds(10); //Aspetta 10us
  ValueSol=digitalRead(Sol); //Associo al valore del pulsante Sol la lettura al pin D8
  delayMicroseconds(10); //Aspetta 10us
  ValueLa=digitalRead(La); //Associo al valore del pulsante La la lettura al pin D7
  delayMicroseconds(10); //Aspetta 10us
  ValueSi=digitalRead(Si); //Associo al valore del pulsante Si la lettura al pin D6
  delayMicroseconds(10); //Aspetta 10us
  
  if(ValueDo==HIGH) //Se il valore del pulsante Do è alto...
  {
    tone(Buzzer,262); //...fai suonare il Buzzer a 262Hz...
    delay(10); //...aspetta 10ms...
    noTone(Buzzer); //...e poi spegni il Buzzer
  }
   
  if(ValueRe==HIGH) //Se il valore del pulsante Re è alto...
  {
    tone(Buzzer,294); //...fai suonare il Buzzer a 294Hz
    delay(10); //...aspetta 10ms...
    noTone(Buzzer); //...e poi spegni il Buzzer
  }
  
  if(ValueMi==HIGH) //Se il valore del pulsante Mi è alto...
  {
    tone(Buzzer,330); //...fai suonare il Buzzer a 330Hz
    delay(10); //...aspetta 10ms...
    noTone(Buzzer); //...e poi spegni il Buzzer
  }
  
  if(ValueFa==HIGH) //Se il valore del pulsante Fa è alto...
  {
    tone(Buzzer,349); //...fai suonare il Buzzer a 349Hz
    delay(10); //...aspetta 10ms...
    noTone(Buzzer); //...e poi spegni il Buzzer
  }
  
  if(ValueSol==HIGH) //Se il valore del pulsante Sol è alto...
  {
    tone(Buzzer,392); //...fai suonare il Buzzer a 392Hz
    delay(10); //...aspetta 10ms...
    noTone(Buzzer); //...e poi spegni il Buzzer
  }
  
  if(ValueLa==HIGH) //Se il valore del pulsante La è alto...
  {
    tone(Buzzer,440); //...fai suonare il Buzzers a 440Hz
    delay(10); //...aspetta 10ms...
    noTone(Buzzer); //...e poi spegni il Buzzer
  }
  
  if(ValueSi==HIGH) //Se il valore del pulsante Si è alto...
  {
    tone(Buzzer,494); //...fai suonare il Buzzers a 349Hz
    delay(10); //...aspetta 10ms...
    noTone(Buzzer); //...e poi spegni il Buzzer
  }
}

Funziona... Solamente che il tempo di delay impostato a 10ms (la miglior soluzione a mio parere comprata alle altre) crea un leggero disturbo in uscita sul buzzer.

Ringrazio tutti quanti per la collaborazione :smiley:

sei un musicista ?
facci sentire qualcosa :slight_smile:

francescobonassi:
Funziona... Solamente che il tempo di delay impostato a 10ms (la miglior soluzione a mio parere comprata alle altre) crea un leggero disturbo in uscita sul buzzer.

Ringrazio tutti quanti per la collaborazione :smiley:

L'importante è che tu sia soddisfatto del risultato, sono sicuro che con un po' d'impegni riuscirai a risolvere anche il disturbo che hai adesso.

@Francesco:
io nutro dei dubbi.... a ripensarci, vedendo il tuo sketch, tu manderesti in riproduzione per 10 millisecondi il tono di una nota, poi lo stacco ecc....Quindi sarebbe un continuo suona per 10 ms/stacca/suona per 10 ms/stacca/suona per 10 ms ecc... se io tengo premuto un pulsante. E' giusto?
Uhm, non mi sembra il modo corretto, però. Tu devi attivare la riproduzione di un tono in base al tasto premuto e poi disattivarlo al rilascio del pulsante, non continuare a fare suona/stacca/suona/stacca mentre vedi il tasto premuto