uscita immediata da un loop o un ciclo

ciao ragazzi... ci sto provando già da un po' ma non riesco a risolvere il problema... vi spiego riassumendo:

il mio programma deve riprodurre una melodia da suspance (quella di fringe :slight_smile: e fino a quì tutto ok... il punto è che la melodia dura un po', quindi prima che finisca passano alcuni secondi, durante i quali il programma rimane bloccato...

quello che vorrei fare è fermare immediatamente la riproduzione dela melodia al cambio di stato di un pulsante... come cappero faccio? cioè mentre la melodia suona, non riesco a leggere il cambio di stato del pulsante, e quindi non riesco a far "accendere il mio ipotetico led", se non alla fine della stessa... qualche idea? ho provato con while, e for, ma niente...

#include "pitches.h"

int ledRosso = 4;
int ledBlu = 5;
int pulsanteRosso = 2;
int pulsanteBlu = 3;
int sirena = 6;
int statoRosso = 0;
int statoBlu = 0;
int melody[] = {
  NOTE_C4, NOTE_G3,NOTE_G3, NOTE_A3, NOTE_G3,0, NOTE_B3, NOTE_C4};
int noteDurations[] = {
  4, 8, 8, 4,4,4,4,4 };




void setup(){
  pinMode (ledRosso, OUTPUT);
  pinMode (ledBlu, OUTPUT);
  pinMode (pulsanteRosso, INPUT);
  pinMode (pulsanteBlu, INPUT);
  pinMode (sirena, OUTPUT);
}



void loop() {
  statoRosso=digitalRead(pulsanteRosso);
  if (statoRosso==HIGH){
    digitalWrite(ledRosso, HIGH);
    for (int i=0; i<=5; i++){
    noTone(sirena);			
    tone(sirena, 880, 200);
    delay(50);
    noTone(sirena);
    tone(sirena, 1000, 200);
    delay(50);
    noTone(sirena);  
    tone(sirena, 1400, 200);
    delay(50);
    }
    delay(1000);
    digitalWrite(ledRosso, LOW);
  }
  else if(statoRosso==LOW){
    melodia();
  }
}

void melodia(){
  for (int thisNote = 0; thisNote < 8; thisNote++) {

    // to calculate the note duration, take one second 
    // divided by the note type.
    //e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.
    int noteDuration = 1000/noteDurations[thisNote];
    tone(sirena, melody[thisNote],noteDuration);

    // to distinguish the notes, set a minimum time between them.
    // the note's duration + 30% seems to work well:
    int pauseBetweenNotes = noteDuration * 1.30;
    delay(pauseBetweenNotes);
    // stop the tone playing:
    noTone(sirena);
  }
}

Utilizza millis() invece dei delay()...

fatto... non funziona comunque... grazie lo stesso :slight_smile:
qualche idea del perchè non funzioni?

Janos:
Utilizza millis() invece dei delay()...

cosa centra il delay con l'intercettare un evento durante il ciclo?

Prendi un pin che abbia l'interrupt, lo attivi, questo resterà in attesa di un cambio anche durante l'esecuzione di una funzione diversa dal loop.

http://gonium.net/md/2006/12/20/handling-external-interrupts-with-arduino/

@Pablos
C'entra eccome. Il micro si blocca, fa esattamente quello che gli dici di fare. Il problema è: perché non riesci a leggere lo stato di un pin? Probabilmente perché sei bloccato dentro un delay a non fare niente, ovvero il micro sta li ad aspettare per un secondo.

@matt
Allora perché dentro il loop vedo alcuni delay(50) e un bel delay(1000)?

@janos

be, quello li sopra è sempre il vecchio codice... ho fatto la prova che mi hai detto e ho cambiato tutti i delay con i millis, ma non funziona...
ma poi scusa, millis non inizia a contare da quando la scheda inizia ad eseguire il programma? a me non serve quello, mi servono i delay per creare quel tipo di suono che voglio io... grazie dell'interessamento comunque! :slight_smile:

@Pablos
ho visto il link che mi hai mandato, sembra buono... mi da un errore il codice che ha postato il tipo però... ho anche aggiunto la libreria che mi dice di aggiungere, ma non mi riconosce GICR |= ( 1 < < INT0);
forse perchè lui usa un atmega8... bo...

Mica li devi sostituire e basta... Sennò quale sarebbe la differenza?
Vedo di buttare giù una bozza, poi la smussi te...

Ti consiglio di leggere qui: http://arduino.cc/en/Tutorial/BlinkWithoutDelay

Scusami se il codice è diventato "largo" ma mi sta troppo sulle scatole che il programma di Arduino usi due spazi per le indentazioni, quindi ne uso uno esterno per scrivere il codice e uso i TAB, ma quando incolli il codice qui poi è un macello.... Ti consiglio di installarti Notepad++ (è gratuito) e incollare li il codice.

#include "pitches.h"

int ledRosso = 4;
int ledBlu = 5;
int pulsanteRosso = 2;
int pulsanteBlu = 3;
int sirena = 6;
int statoRosso = 0;
int statoBlu = 0;
int melody[] = {
	NOTE_C4, NOTE_G3,NOTE_G3, NOTE_A3, NOTE_G3,0, NOTE_B3, NOTE_C4};
int noteDurations[] = {
	4, 8, 8, 4,4,4,4,4 };




void setup(){
	pinMode (ledRosso, OUTPUT);
	pinMode (ledBlu, OUTPUT);
	pinMode (pulsanteRosso, INPUT);
	pinMode (pulsanteBlu, INPUT);
	pinMode (sirena, OUTPUT);
}


unsigned long int inizioTimer;
byte stato;
bool toneImpostato;
byte contatore;
bool fineCiclo;

void loop() {
	statoRosso=digitalRead(pulsanteRosso);
	if (statoRosso==HIGH){
		if (!fineCiclo) {
			digitalWrite(ledRosso, HIGH);
			if (contatore < 5) {
				switch (stato) {
					case 0:	
						if (!toneImpostato) {
							tone(sirena, 880, 200);
							toneImpostato = true;
							inizioTimer = millis();
						};
						if (millis() - inizioTimer >= 50) {
							stato++;
							toneImpostato = false;
						};
						break;
						
					case 1;
						if (!toneImpostato) {
							tone(sirena, 1000, 200);
							toneImpostato = true;
							inizioTimer = millis();
						};
						if (millis() - inizioTimer >= 50) {
							stato++;
							toneImpostato = false;
						};
						break;
						
					case 2:
						if (!toneImpostato) {
							tone(sirena, 1400, 200);
							toneImpostato = true;
							inizioTimer = millis();
						};
						if (millis() - inizioTimer >= 50) {
							stato = 0;
							contatore++;
							toneImpostato = false;
						};
						break;
					
					default:
						stato = 0;
						contatore = 0;
						toneImpostato = false;
						break;
					
				}
			}
			else {
				if (!toneImpostato) {		//Ho riciclato una variabile che in questa parte del codice non serve
					inizioTimer = millis();	//Qui ne ho riciclata un'altra
					toneImpostato = true;
				}
				if (toneImpostato && (millis() - inizioTimer >= 1000)) {
					digitalWrite(ledRosso, LOW);
					fineCiclo = true;
				};
			};
		}
		//Se non metti questa riga lui ti esegue a diritto
		//Così invece fa un ciclo e prima di fare il successivo aspetta che tu alzi il tasto
		else (!digitalWrite(ledRosso, HIGH)) fineCiclo = false;		//Se non metti questa riga lui ti esegue a diritto
	}
	else {
		melodia();
	}
}

void melodia(){
  for (int thisNote = 0; thisNote < 8; thisNote++) {

    // to calculate the note duration, take one second 
    // divided by the note type.
    //e.g. quarter note = 1000 / 4, eighth note = 1000/8, etc.
    int noteDuration = 1000/noteDurations[thisNote];
    tone(sirena, melody[thisNote],noteDuration);

    // to distinguish the notes, set a minimum time between them.
    // the note's duration + 30% seems to work well:
    int pauseBetweenNotes = noteDuration * 1.30;
    delay(pauseBetweenNotes);
    // stop the tone playing:
    noTone(sirena);
  }
}

non centra perchè lui è dentro a una funzione quando ha bisogno di leggere la pressione del bottone che impegna il suo tempo, non ha mica un delay da 10 minuti

loop()

Vai alla funzione 1()
Vai alla funzione 2()
Vai alla funzione 3()
Vai alla funzione 4()
leggi il pulsante

il pulsante lo legge solo quando ha terminato le 4 funzioni, ci vuole qualcosa che intercetti il pulsante interrompendo per un istante tutto quello che il micro sta facendo, questo lo fa solo attivando un interrupt

P.S. Per usare gli interrupt leggi qui:
http://arduino.cc/en/Reference/AttachInterrupt
http://arduino.cc/en/Reference/DetachInterrupt

@Pablos
Prima di dare suggerimenti agli altri ti consiglio di approfondire tu stesso le tue conoscenze in materia, visto che non sai la differenza fra delay() e millis() e che suggerisci improbabili esempi sulla gestione degli interrupt quando ci sono le apposite funzioni... Inoltre qui, anche usando l'interrupt, se sei bloccato in un dealy non ne esci.
P.S. Leggi per bene il mio codice e poi parla... Inoltre consiglio anche a te di leggere http://arduino.cc/en/Tutorial/BlinkWithoutDelay

Si janos vado a studiare, intanto tu fagli usare un millis() per 50ms

Per te sono un'inezia 50ms ma nella realtà dell'elettronica 50ms sono un tempo abissale. Pensa che il processore esegue un'istruzione ogni 62ns, quindi con un delay(50) fai stare fermo il processore per ben 806.000 operazioni. Inoltre quello che conta di più è imparare il metodo, quando sai come usarlo poi decidi tu se e quando usarlo...

pablos:
Si janos vado a studiare, intanto tu fagli usare un millis() per 50ms

ai fini pratici ha ragione pablos ai fini teorici ha ragione janos :grin: ora non litigate o vi mando al quizzettone :wink:

ehm i delay NON bloccano gli interrupt, altrimentio non usciresti mai dalla delay visto che è basata su un interrupt del timer1 :slight_smile:

Si janos vado a studiare, intanto tu fagli usare un millis() per 50ms

Per te sono un'inezia 50ms ma nella realtà dell'elettronica 50ms

ma un pulsante premuto (da un essere umano) per meno di 50ms è abbastanza impossibile nella realtà..

Il problema è che quei delay(50) sono in loop e seguiti anche da delay(1000), il che rende il tempo della funzione troppo alto.

Io sono d'accordo con janos di usare la millis() come per l'esempio blinkWithoutDelay;
in particolare , mio caro matt-korban, se non lo fai, finchè l'arduino suona NON legge i pulsanti.

il sistema consigliato da pablos non è errato, ma un'alternativa assai valida: l'uso della millis costringe a riscirvere buona parte del codice, con memorizzazione dello stato tra un loop e l'altro (macchina a stati), e permette di interrompere a metà l'esecuzione di una canzoncina per effettuarne un altra.
l'interrupt permette di sostituire la digitalRead con una variabile booleana settata dall'interrupt e azzerata dopo il suo uso nel loop(l'interrupt conterrà al suo interno un algoritmo anti-bounce, o un anti-bounce HardWare), ma essa non verrà usata finchè il loop corrente è finito, quindi la canzincina deve finire.

kiss

lesto:
ehm i delay NON bloccano gli interrupt, altrimentio non usciresti mai dalla delay visto che è basata su un interrupt del timer1 :slight_smile:

Sono d'accordo con te, ma se lanci un delay(1000) e avviene un interrupt tu vai a eseguire l'interrupt. Il problema è che da interrupt non puoi fare niente perché una volta uscito torni a finire quanto ti resta del delay...

Diciamo che una soluzione intelligente, vista l'applicazione, sarebbe stata di lasciare i delay(50) e sostituire il delay(1000) con millis()...

approvo su tutta la linea :grin:

cioè ma io posso scatenare un casino epocale per un quizzone di m____a?
quindi in parole povere mio caro lesto, quello che dovrei fare è???
usare gli interrupt oppure sostituire i delay con dei millis?

da quello che ho capito non basta sostituire le voci delay con millis, devo pure modificare il programma, giusto?

ma poi ragazzi il delay 1000 non mi da nessun fastidio in quanto tiene il led acceso per un secondo dopo la sirena, e questo mi sta bene... quello che blocca il programma è l'esecuzione della canzoncina prima che venga premuto il pulsante... una volta premuto il pulsante infatti, la canzoncina si ferma e parte una "sirena" che dura qualcosa come un secondo, e si accende un led...

Mi sono preso la briga di prendere il tuo codice e modificarlo, ti ho dato tutti i link per documentarti, che vuoi di più?