Start/Stop esecuzione programma con un solo pulsante

Ciao a tutti.
Mi sto cimentando con la costruzione di un midi sequencer e sto utilizzando Arduino Mega.

Vi allego il codice prima ancora di chiarire la mia richiesta:
qui inizializzo le variabili e definisco le funzioni "setTreadleValue" e "setProgram" che inviano la sequenza midi al dispositivo esterno.

#define START 2
#define UP 3
#define DOWN 3

int val1 = 0;
int old_val1 = 0;
int state1 = 0;

int val2 = 0;
int old_val2 = 0;
int state2 = 0;

int val3 = 0;
int old_val3 = 0;
int state3 = 0;

int i = 0;
int pgm = 1;
int function = 0;

void setTreadleValue(uint8_t n) {
Serial.write((uint8_t)0xB0); //TYPE: control change
Serial.write((uint8_t)0x0B); //CONTROLLER NUMBER 11: expression pedal
Serial.write((uint8_t)n);
}
 
void setProgram(uint8_t p) {
Serial.write((uint8_t)0xC0);
Serial.write((uint8_t)p);
}

void setup ();

void loop ();

Tralasciando il setup vi allego il ciclo Loop:

void loop()

{
val1 = digitalRead(START);
val2 = digitalRead(UP);
val3 = digitalRead(DOWN);

if( (val1==HIGH) && (old_val1==LOW) )
  {
    state1=1-state1;
    delay(10);
    old_val1 = val1;
  }

if( (val2==HIGH) && (old_val2==LOW) )
  {
    state2=1-state2;
    delay(200);
    old_val2 = 0;
  }

if( (val3==HIGH) && (old_val3==LOW) )
  {
    state3=1-state3;
    delay(200);
    old_val3 = 0;
  }

if( (state2 == 1) && (function == 0) )
  {
    pgm = pgm+1;
    state2 = 0;
    delay(50);
  }

if( (state3 == 1) && (function == 0) )
  {
    pgm = pgm-1;
    state3 = 0;
    delay(50);
  }

if (state1 == 1)
  {
    start();
  }

per poi approdare allo start() richiamato dalla condizione if (state1 == 1) all'interno del loop.

void start() {

  if(pgm == 1)
  {
    Whammy_preset();
  }
  if(pgm == 2)
  {
    MOTP();
  }
}

Come soluzione di partenza ho impostato il pulsante di Start (sul pin2) in modo che quando si trova a livello logico alto, modifica la variabile "state1" e fa partire la relativa sequenza midi richiamata attraverso le variabili pgm1...2..3....

Vorrei capire come possibile utilizzare lo stesso identico pulsante anche per arrestare la sequenza, considerando che:
a) La sequenza midi può durare anche diversi minuti quindi non posso attendere la completa esecuzione della stessa per rientrare all'interno del loop ed effettuare un controllo di state1
b) Nel momento dell'arresto Arduino deve mettersi in stand-by e mantenere il programma settato (pgm1 o pgm2....)
c) Durante questo stato di stand-by, deve essere possibile selezionare un nuovo programma oppure rimanere sullo stesso ancora selezionato ma in ogni caso la sequenza deve sempre ripartire dall'inizio alla pressione del pulsante di start

Solo come "pagliativo" sto utilizzando per lo stop un pulsante collegato al pin di reset ma la soluzione non è né elegante né quella voluta né sto utilizzando un solo pulsante.
Avevo pensato anche ad un interrupt ma non so se si sposa bene con la funzione di start (che non rappresenterebbe di fatto un interrupt) né sono certo non mi possa creare dei problemi relativi al rimbalzo del pulsante.

Mi potreste suggerire qualche soluzione?

Grazie
Max76

Non puoi usare una variabile di stato associata al pulsante ed un paio di if ? ... ogni volta che premi il pulsante cambi stato alla variabile, e poi con gli if in base allo stato della variabile fai cose diverse ...

Oppure ho capito male io ?

Il problema è che nel momento in viene premuto il pulsante di start, l'esecuzione del programma viene "dirottata" all'interno di una sequenza midi (che può durare anche alcuni minuti) e dovrei attendere l'esecuzione completa di questa per poi "ritornare" nuovamente all'interno di void loop() e verificare lo stato della variabile. (o sto forse sbagliando?)
Magari una soluzione è di inserire degli IF di controllo all'interno della sequenza midi per verificare lo stato del pulsante ma onestamente mi sembra un accrocchio più che la soluzione.

Allego una sequenza midi giusto per capire cosa intendo.

#define DLY 135

void MOTP()
{

// start cicle: 4 song + 2 to start
for(int index=1; index=6; index++) {

//-8
setProgram(7);
setTreadleValue(127);
delay(DLY);
//0
setTreadleValue(0);
delay(DLY);
//+8
setProgram(1);
setTreadleValue(127);
delay(DLY);
//0
setTreadleValue(0);
delay(DLY);
//+8
setProgram(1);
setTreadleValue(127);
delay(DLY);
//0
setTreadleValue(0);
delay(DLY);
//-8
setProgram(7);
setTreadleValue(127);
//0
setTreadleValue(0);
delay(DLY);
//+8
setProgram(1);
setTreadleValue(127);
delay(DLY);
//0
setTreadleValue(0);
delay(DLY);
//+8
setProgram(1);
setTreadleValue(127);
delay(DLY);
//0
setTreadleValue(0);
delay(DLY);
//-8
setProgram(7);
setTreadleValue(127);
//0
setTreadleValue(0);
delay(DLY);
//+8
setProgram(1);
setTreadleValue(127);
delay(DLY);
//0
setTreadleValue(0);
delay(DLY);
}

for (int index=1; index=4; index++) {

//+8
setProgram(1);
setTreadleValue(127);
delay(DLY);
//0
setTreadleValue(0);
delay(DLY*3);
//+8
setProgram(1);
setTreadleValue(127);
delay(DLY);
//0
setTreadleValue(0);
delay(DLY);  
                                      }
for(int verse=1; verse=1; verse++) {                                      
//+8
setProgram(1);
setTreadleValue(127);
delay(DLY);
//0
setTreadleValue(0);
delay(DLY*3);
     }
                                      
for(int verse=1; verse=3; verse++) {
//-8
setProgram(7);
setTreadleValue(127);
//0
setTreadleValue(0);
delay(DLY);
//+8
setProgram(1);
setTreadleValue(127);
delay(DLY);
//0
setTreadleValue(0);
delay(DLY);
//+8
setProgram(1);
setTreadleValue(127);
delay(DLY);
//0
setTreadleValue(0);
delay(DLY);
//-8
setProgram(7);
setTreadleValue(127);
//0
setTreadleValue(0);
delay(DLY);
//+8
setProgram(1);
setTreadleValue(127);
delay(DLY);
//0
setTreadleValue(0);
delay(DLY);
//+8
setProgram(1);
setTreadleValue(127);
delay(DLY);
//0
setTreadleValue(0);
delay(DLY);
//-8
setProgram(7);
setTreadleValue(127);
//0
setTreadleValue(0);
delay(DLY);
//+8
setProgram(1);
setTreadleValue(127);
delay(DLY);
//0
setTreadleValue(0);
delay(DLY);
}

for(int verse=1; verse=2; verse++) {
//+8
setProgram(1);
setTreadleValue(127);
delay(DLY);
//0
setTreadleValue(0);
delay(DLY);
//-8
setProgram(7);
setTreadleValue(127);
//0
setTreadleValue(0);
delay(DLY);
//+8
setProgram(1);
setTreadleValue(127);
delay(DLY);
//0
setTreadleValue(0);
delay(DLY);
//+8
setProgram(1);
setTreadleValue(127);
delay(DLY);
//0
setTreadleValue(0);
delay(DLY);
//-8
setProgram(7);
setTreadleValue(127);
//0
setTreadleValue(0);
delay(DLY);
//+8
setProgram(1);
setTreadleValue(127);
delay(DLY);
//0
setTreadleValue(0);
delay(DLY);
//-8
setProgram(7);
setTreadleValue(127);
//0
setTreadleValue(0);
delay(DLY);
//+8
setProgram(1);
setTreadleValue(127);
delay(DLY);
//0
setTreadleValue(0);
delay(DLY);
}


for(int verse=1; verse=2; verse++) {
//-8
setProgram(7);
setTreadleValue(127);
//0
setTreadleValue(0);
delay(DLY);
//+8
setProgram(1);
setTreadleValue(127);
delay(DLY);
//0
setTreadleValue(0);
delay(DLY);
//+8
setProgram(1);
setTreadleValue(127);
delay(DLY);
//0
setTreadleValue(0);
delay(DLY);
//-8
setProgram(7);
setTreadleValue(127);
//0
setTreadleValue(0);
delay(DLY);
//+8
setProgram(1);
setTreadleValue(127);
delay(DLY);
//0
setTreadleValue(0);
delay(DLY);
//+8
setProgram(1);
setTreadleValue(127);
delay(DLY);
//0
setTreadleValue(0);
delay(DLY);
//-8
setProgram(7);
setTreadleValue(127);
//0
setTreadleValue(0);
delay(DLY);
//+8
setProgram(1);
setTreadleValue(127);
delay(DLY);
//0
setTreadleValue(0);
delay(DLY);
}

for(int verse=1; verse=1; verse++) {
//+8
setProgram(1);
setTreadleValue(127);
delay(DLY);
//0
setTreadleValue(0);
delay(DLY);
//-8
setProgram(7);
setTreadleValue(127);
//0
setTreadleValue(0);
delay(DLY);
//+8
setProgram(1);
setTreadleValue(127);
delay(DLY);
//0
setTreadleValue(0);
delay(DLY);
//+8
setProgram(1);
setTreadleValue(127);
delay(DLY);
//0
setTreadleValue(0);
delay(DLY);
//-8
setProgram(7);
setTreadleValue(127);
//0
setTreadleValue(0);
delay(DLY);
//+8
setProgram(1);
setTreadleValue(127);
delay(DLY);
//0
setTreadleValue(0);
delay(DLY);
//+8
setProgram(1);
setTreadleValue(127);
delay(DLY);
//0
setTreadleValue(0);
delay(DLY);
//-8
setProgram(7);
setTreadleValue(127);
//0
setTreadleValue(0);
delay(DLY);
}

}

Ciao, non sono eperto di sequenze MIDI ma visto che tra un tono e l'altro hai inserito i delay necessari alla corretta esecuzione del brano suppongo e non fai nessun controllo una volta richiamata tale funzione non ne esci più finchè non termina il brano.
Non so se è una strada percorribile ma credo che se tu definissi due array che contengono i vari valori di setProgram e setTreadleValue e dentro alla funzione con un while cicli finchè non hai terminato l'array o il pulsante non è stato premuto alla peggio attendi un unico delay e poi verificando lo stato del pulsante potresti uscire dal cliclo e quindi dalla funzione immediatamente dopo rendendo il tempo d'arresto immediato "umanamente" parlando

Grazie dell'idea fabpolli.
Non sono certo possa funzionare poiché quella allegata era una sequenza con un delay fisso. Potrei avere sequenze dove il delay varia all'interno della stessa sequenza. Ho paura che un array diventerebbe troppo complesso o meglio mi toccherebbe dividere una sequenza in multipli array quante sono le variazioni del valore di delay.

Se non è un problema di dimensioni di array ti basta un terzo array contentente i delay variabili e sei a posto

Oppure, se come dici l'esecuzione è bloccante, usare un interrupt per il pulsante

Grazie Brunello.
Secondo te posso con un unico pulsante gestire lo start e lo stop tenendo solo lo stop che chiamal'interrupt? (magari sfruttando il fronte d'onda discendente)

Posso sempre applicare il de-bounce al pulsante durante la chiamata di interrupt? (mi era parso di trovare in rete che non fosse possibile)

Grazie

Saro' breve perche' è la terza volta che lo riscrivo e le prime due si sono perse chissa' dove..

Secondo te posso con un unico pulsante gestire lo start e lo stop tenendo solo lo stop che chiamal'interrupt

Si che puoi farlo, basta che abiliti l'Interrupt solo dopo che sei entrato nella routine di start

Posso sempre applicare il de-bounce al pulsante durante la chiamata di interrupt?

In genere si usa un debounce hardware


anche se in teoria è possibile usarne uno software, controllando il tempo trascorso tra due interrupt, ma non nella stessa routine di Interrupt che deve essere il piu' veoce possibile
Comunque a te non serve, se lo vuoi usare come dici, visto che al primo Interrupt , disabiliti l'interrupt stesso e quindi ignori tutti i successivi rimbalzi

I debounce hardware sarebbe bene imparare ad usarli sempre, su qualsiasi ingresso collegato a pulsanti o interruttori ... non avete idea di quanti problemi risolvono, quel paio di componenti in piu :smiley: (meglio usare 220 ohm al posto di 1K in serie, il resto e' ok)

Una domanda Brunello ma esiste un sistema che non conosco per fare in modo che a seguito di un interrupt esca dalla funzione (nel caso specifico quella che suona il midi) e rientri nel loop o comunque all'interno della funzione dopo i vari delay dovrà essere controllato il flag e fare una return?

Non sono sicuro se si puo ... ma se dall'interno della routine interrupt, dopo aver resettato la flag, richiami il loop() ? ... o si incricca a fare in questo modo ?

Una domanda Brunello ma esiste un sistema che non conosco per fare in modo che a seguito di un interrupt esca dalla funzione (nel caso specifico quella che suona il midi) e rientri nel loop o comunque all'interno della funzione dopo i vari delay dovrà essere controllato il flag e fare una return?

No, che io sappia non c'è. con l'interrupt puoi, in qualunque momento, eseguire una funzione, settare un flag etc, ma devi comunque controllarlo all'interno della funzione che stia eseguendo.
Si rifa' a quello che dicevi tu prima

Mi riferivo invece a quello che diceva

come "pagliativo" sto utilizzando per lo stop un pulsante collegato al pin di reset

Se gli va bene un reset , basta che nella routine di interrupt esegua un soft reset

  ......
  attachInterrupt(digitalPinToInterrupt( pin ), reset_soft, CHANGE);
  .........

void reset_soft() {
  asm("jmp 0");
}

Non è bello, però ha i suoi vantaggi... non deve modficare lo sketch con i vari controlli

Farò qualche esperimento e vi farò sapere. Intanto grazie a tutti per i preziosi suggerimenti.

Max