Un bell'impasse con lo switch case.. Come fare?

Salve a tutti!
Sono una newbie e sono sicura che questa domanda sia stata già posta molte volte, ma pur avendo spulciato decine di post e metodi non ne sono venuta a capo.

Il problema è semplice:

In un determinato caso di uno switch case, devo trovare un modo con cui il programma continui in loop a verificare se tre condizioni sono tutte soddisfatte in contemporanea, prima di stampare una cosa nel monitor seriale e uscire dal caso. Ora come ora la condizione viene verificata una volta sola. Se quando il programma entra nel caso e tutte e tre le condizioni sono soddisfatte, stampa quello che deve stampare. Altrimenti, ovviamente, no.

Ho provato con while(1){//cose da fare in loop} ma in questo caso non è possibile uscire dal loop. Ho provato anche con il do/while e ovviamente non ha funzionato.

Come posso risolvere o aggirare il problema?

void loop() {

  if (Serial.available() > 0)  {
    int mode = Serial.read();

    switch (mode) {
      case '1':
        Serial.println("In Case 1");
        if (digitalRead(magneticPin1) == 1 && digitalRead(magneticPin2) == 1 && digitalRead(magneticPin3) == 1) {
          Serial.println("<GO>"); 
        } else {continua a controllare finchè le condizioni in if diventano vere}
        break;
      case '2':
        Serial.println(digitalRead(magneticPin1));
    }
  }
}

Sarà semplice il problema ma per me non si capisce molto.

la loop() è una funzione richiamata di continuo, quello che il core arduino nasconde è il classico main() di un programma C

void main()
{ setup();
  while(1) loop();
}

Quindi tutto quello che c'e' nel codice della loop() è già in un ciclo infinto.

Perciò... come puoi stare dentro al case '1' di continuo se ad ogni giro di loop() rileggi da seriale ??
Di continuo spedisci '1' ??

Ora come ora la condizione viene verificata una volta sola.

Una sola volta per ogni volta che scrivi nel serial monitor il carattere 1.

Anche se inserissi del codice all’interno del blocco if (Serial.available()…
verrebbe eseguito una volta per ogni carattere inserito.

Ciao.

nid69ita:
Perciò... come puoi stare dentro al case '1' di continuo se ad ogni giro di loop() rileggi da seriale ??
Di continuo spedisci '1' ??

Grazie per la risposta!
Io, appunto, vorrei evitare di spedire di continuo '1'.
Quello che chiedo è un modo per fare un loop dentro uno dei casi di switch case in pratica

bool enableSensorCheck = false;
bool checkMagneticSensor() {
    return (digitalRead(magneticPin1) & digitalRead(magneticPin2) & digitalRead(magneticPin3);
}


void loop() {

  if (enaleSensorCheck && checkMagneticSensor()) {
     Serial.println("<GO>");
  }

  if (Serial.available() > 0)  {
    int mode = Serial.read();

    switch (mode) {
      case '1':
        Serial.println("In Case 1");
        enableCheckSensor = true;
        break;
      case '2':
        Serial.println(digitalRead(magneticPin1));
    }
  }
}

Prova questa soluzione.

Ciao.

La condizione che interrompe il loop che vuoi creare va inserita nelle parentesi del while.

Io però eviterei a prescindere un approccio di questo tipo. Ogni while che inserisci, se non ben gestito, può diventare un buco nero dal quale l'esecuzione del programma non esce più oltre ad essere bloccante.

cotestatnt:
Io però eviterei a prescindere un approccio di questo tipo. Ogni while che inserisci, se non ben gestito, può diventare un buco nero dal quale l’esecuzione del programma non esce più oltre ad essere bloccante.

Grazie per la risposta!
Che approccio consiglieresti, volendo evitare lo switch…case e while?
Io ho praticamente tre casi, diciamo “modalità di funzionamento” che cambiano in base ad un comando ricevuto dal monitor seriale. Ogni modalità deve essere un loop a sè, che verifica continuamente se determinate condizioni sono soddisfatte

Infatti, per adesso,ho risolto così, anche se mi sembra un modo molto rozzo

while (x == '1') {
      Serial.println("In Case 1");
      if (digitalRead(magneticPin1) == 1 && digitalRead(magneticPin2) == 1 && digitalRead(magneticPin3) == 1) {
        Serial.println("<GO>");
        break;
      }
    }

Mancano alcune specifiche per poter rispondere in modo accurato, comunque è un problema che solitamente si risolve con una macchina a stati finiti (cerca sul forum per maggiori dettagli ci sono molte discussioni a riguardo usando anche MSF o FSM)
in linea di massima potresti fare una cosa del tipo (ma è un esempio fatto senza test):

if (Serial.available() > 0)  {
    int mode = Serial.read();
    bool newState = true;
}
switch(mode)
{
   case 0:
      if(newState)
      {
         serial.print(F("mode zero"));
         newState = false;
      }
      ...cose da fare finché non cambia lo stato...
   break;
   case 1:
     if(newState)
      {
         serial.print(F("mode one"));
         newState = false;
      }
      ...cose da fare finché non cambia lo stato...

Sara_kon:
Che approccio consiglieresti, volendo evitare lo switch...case e while?

L'approccio che ti ha già suggerito fabpolli, anche se organizzato in maniera leggermente diversa.
Le tre modalità di funzionamento le "incapsuli" in 3 funzioni distinte che richiami nello switch/case e da quanto ho capito hai bisogno che vengano eseguite di continuo quindi la variabile newState non è necessaria.
Inoltre in questi casi io uso sempre delle enumerazioni (enum{}) per avere un'idea immediata dei diversi stati.

void funzione1(){ cose da fare nello stato 1 }
void funzione2(){ cose da fare nello stato 2 }
void funzione3(){ cose da fare nello stato 3 }

enum states {NODEF, STATE1, STATE2, STATE3};
int actualState = NODEF;

void loop(){
  if (Serial.available() > 0)  {
    // Cosi va bene per provare rapidamente, ma meglio prevedere un minimo di controllo su quanto ricevuto
    actualState = Serial.read();   
  }
  switch(actualState)
  {
    case STATE1:
      funzione1();
      break;
    case STATE2:
      funzione2();
      break;
    case STATE3:
      funzione3();
      break;
    default:
      break;
  }
}

La variabile newState l'ho inserita perché mi pareva di aver capito che volsse stampare su monitor seriale qualcosa solo una volta al cambio di stato, se non è così concordo che sia superflua

Che approccio consiglieresti, volendo evitare lo switch…case e while?

Puoi poi aggiungere una serie di "else if " per altri casi.

if (digitalRead(magneticPin1) == 1 && digitalRead(magneticPin2) == 1 && digitalRead(magneticPin3) == 1) {
  Serial.println("<GO>");
} else if ( caso .....){
  Serial.println("In Case 1");
} else {
..........
}

(non provato).

Se quello che vuoi fare è lungo e complicato il suggerimento di Contestant mi sembra più elegante.
Così eviti il while e lo switch,

Saluti
Enrico

Io aggiungerei

Per pochi casi:
If else if

Per un numero intermedio di casi
Switch

Per tanti, tantissimi casi
Array di puntatori a funzione

Oppure

Per condizioni non necessariamente numeriche intere e/o complesse
If else

Per condizioni esclusivamente numeriche intere e magari nemmeno numericamente consecutive
Switch

Per condizioni esclusivamente numeriche E certamente numericamente consecutive
Array di puntatori a funzioni

Standardoil:
Per tanti, tantissimi casi
Array di puntatori a funzione

Una mia curiosità che esula un po' dall'argomento: perché ritieni che l'array di puntatori a funzioni sia da preferire solo se le condizioni sono molte?
Io la trovo una soluzione elegantissima e cosi a naso direi che è più "prestante" in ogni caso a prescindere dal numero delle condizioni.
L'unico dubbio che ho, è possibile passare eventuali parametri alle funzioni ed in che modo?

Si, puoi passare i parametri, ma le funzioni devo avere la stessa "impronta" ovvero stesso numero e tipo di parametri.
Almeno in C.

Io ho capito che non ho capito come si deve comportare il programma, per cui ne scrivo un’altra versione magari ci prendo.

Ok, mi sono perso questo commento:

Io, appunto, vorrei evitare di spedire di continuo ‘1’.

Il codice seguente fa quello che desideri.

int mode;
void loop() {

    if (Serial.available() > 0)  {
     mode = Serial.read();
    }

    switch (mode) {
      case '1':
        Serial.println("In Case 1");
        if (digitalRead(magneticPin1) == 1 && digitalRead(magneticPin2) == 1 && digitalRead(magneticPin3) == 1) {
          Serial.println("<GO>");
        } else {continua a controllare finchè le condizioni in if diventano vere}
        break;
      case '2':
        Serial.println(digitalRead(magneticPin1));
   }
  
}

In questo modo dopo avere premuto 1 viene eseguito in loop il case 1, premendo 2 viene eseguito il case 2 in loop e premendo un altro numero nessuno dei case viene eseguito.

Ciao.

Sara_kon:
Grazie per la risposta!
Infatti, per adesso,ho risolto così, anche se mi sembra un modo molto rozzo

while (x == '1') {

@Sara, come ti hanno detto sopra, non cercare di forzare cicli all'interno della loop().
La loop() è già un ciclo. Devi ragionare in maniera diversa. Non come se la loop() fosse un programma che inizia e finisce.
Quindi con delle if ed anche eventuali variabili di stato (che accendi/spegni quando serve) eviti o forzi ingresso in certi rami di if.

Lo consiglio solo per tanti casi solo perché è leggermente meno intuitivo

Sulla sua eleganza non si discute, ne sono entusiasta

nid69ita:
Si, puoi passare i parametri, ma le funzioni devo avere la stessa “impronta” ovvero stesso numero e tipo di parametri.
Almeno in C.

Se i parametri sono diversi immagino che una possibile soluzione potrebbe essere raggrupparli in delle struct{} e poi passare alle n-funzioni un puntatore a struct.
Che ne dici?

Edit:

Standardoil:
Lo consiglio solo per tanti casi solo perché è leggermente meno intuitivo

Si in effetti potrebbe “intimorire” se si è poco pratici con puntatori e affini

Maurotec:
Io ho capito che non ho capito come si deve comportare il programma, per cui ne scrivo un'altra versione magari ci prendo.

Hey, grazie mille!
Ho provato sia con il primo suggerimento che mi hai dato, ma in quel caso mi stampava il "Go" anche al di fuori del case '1', mentre questo è quello che ho provato a fare e poi ho aggirato preferendo il while, perchè non saprei come trascrivere quel "continua a controllare" in else.
Ora proverò la soluzione proposta da Contestatnt che mi pare quella più alla mia portata (forse), e sicuramente mi vado a vedere la programmazione a stati, visto che, da brava ignorantella, non ne avevo mai sentito parlare
è sempre molto bello confrontarsi qui, grazie a tutti! :smiley:

>Sara_kon: Quando si quota un post, NON è necessario riportarlo (inutilmente) tutto; bastano poche righe per far capire di cosa si parla ed a cosa ci si riferisce, inoltre, se si risponde al post immediatamente precedente, normalmente NON è necessario alcun “quote” dato che è sottinteso. :slight_smile:

Gli utenti da device “mobile” (piccoli schermi) ringrazieranno per la cortesia :wink:

Guglielmo

P.S.: Ho troncato io il “quote” del tuo post qui sopra :wink: