PORTA SERIALE PIU FUNZIONI

Buonasera a tutti

io sto facendo un progetto dove attraverso la porta seriale do dei comandi ad arduino, ad es. se digito 0 succede qualcosa e se digito 1 succede qualcos'altro, vorrei aggiungere altri comandi come ad es. 2 fa qualcosa'altro ancora, posto il mio codice qui sotto (quello con due comandi) dove è stata utilizzata la funzione if .... else, per più di due comandi non so che funzione usare.

int ledPin = 13; // the number of the LED pin

void setup() {
Serial.begin(9600); // set serial speed
pinMode(ledPin, OUTPUT); // set LED as output
digitalWrite(ledPin, LOW); //turn off LED
}


void loop(){
while (Serial.available() == 0); // do nothing if nothing sent
int val = Serial.read() - '0'; // deduct ascii value of '0' to find numeric value of sent number

if (val == 1) { // test for command 1 then turn on LED
Serial.println("LED on");
digitalWrite(ledPin, HIGH); // turn on LED
}
else if (val == 0) // test for command 0 then turn off LED
{
Serial.println("LED OFF");
digitalWrite(ledPin, LOW); // turn off LED
}
else // if not one of above command, do nothing
{
//val = val;
}
Serial.println(val);
Serial.flush(); // clear serial port
}

In attesa di una vostra risposta.
Cordiali Saluti

codice_arduino.ino (753 Bytes)

  1. con tanti if
    if a==0 fai qualcosa
    if a==1 fai qualcosa d'altro
    if a==2 fai ancora qualcosa d'altro

  2. con switch case vedi
    https://www.arduino.cc/en/Reference/SwitchCase

Ciao Uwe

Ciao UWE,
Basta che metto tanti if uno sotto l'altro o devo chiudere sempre con else?
Un'altra domanda, else if a cosa serve?
Ti ringrazio anticipatamente per la risposta.
Ciao
Edo

Sarebbe meglio usare il costrutto else if se una condizione deve negare le successive

if(a>2){ blocco 1 } //se a è maggiore di 2 esegu il il blocco 1
else //Se invece a non è maggiore di 2
if(a>1){ blocco 2 }//ma è maggiore di 1 esegui blocco 2
else //Se invece a non è ne maggiore di 2 ne maggiore di uno
if(a>0){ blocco 3 } //ma è magiore di 0 esegui blocco 3
else {blocco 4}//Se invece a non è ne maggiore di 2 ne maggiore di 1 ne maggiore di 0 esegui il blocco 4

Questa è in basic ma si dovrebbe capire lo stesso

In poche parole hai il costrutto if(condizione) {blocco se condizione true} else {blocco se condizione è false}
Puoi concatenarli all'infinoito gli if ed else.

Buonasera

Innanzitutto la ringrazio per la tempestiva risposta,
ho seguito il suo consiglio ed ho messo tanti blocchi if,

nel seguente modo:

void loop() {
while (Serial.available() == 0); // do nothing if nothing sent
int val = Serial.read() - '0'; // deduct ascii value of '0' to find numeric value of sent number

if(val>10){ Serial.println("LED6 on");
digitalWrite(ledPin6, HIGH); 
}
else 

if(val>9){ Serial.println("LED6 OFF");
digitalWrite(ledPin6, LOW); // turn off LED 
}
else 

if(val>8){ Serial.println("LED5 on");
digitalWrite(ledPin5, HIGH); 
}
else 

if(val>7){ Serial.println("LED5 OFF");
digitalWrite(ledPin5, LOW); // turn off LED 
}
else 


if(val>6){ Serial.println("LED4 on");
digitalWrite(ledPin4, HIGH); 
}
else 

if(val>5){ Serial.println("LED4 OFF");//6
digitalWrite(ledPin4, LOW); // turn off LED 
}
else 


if(val>4){ Serial.println("LED3 on");//5
digitalWrite(ledPin3, HIGH); 
}
else 

if(val>3){ Serial.println("LED3 OFF");//4
digitalWrite(ledPin3, LOW); // turn off LED 
}
else 
if(val>2){ Serial.println("LED1 on");
digitalWrite(ledPin, HIGH); 
} //se a è maggiore di 2 esegu il il blocco 1
else  //Se invece a non è maggiore di 2

if(val>1){ Serial.println("LED1 OFF");
digitalWrite(ledPin, LOW); // turn off LED 
}//ma è maggiore di 1 esegui blocco 2
else //Se invece a non è ne maggiore di 2 ne maggiore di uno

if(val>0){Serial.println("LED2 on");
digitalWrite(ledPin2, HIGH); 
} //ma è magiore di 0 esegui blocco 3

else {Serial.println("LED2 OFF");
digitalWrite(ledPin2, LOW);
}//Se invece a non è ne maggiore di 2 ne maggiore di 1 ne maggiore di 0 esegui il blocco 4
Serial.println(val);
Serial.flush(); // clear serial port

}

Arrivato però al valore 10, composto da due cifre, 1 e 0, secondo me arduino lo legge come due numeri separati 1 e 0 e non 10,
come posso fare per ovviare questo problema?

Mi scuso per il disturbo,
Buonanotte
Edo

Il maggiore era un esempio, nel tuo caso è corretto il doppio uguale. Se vuoi usare più di 10 funzioni allora devi utilizzare 2 caratteri per ogni chiamata, quindi 01, 02, 03, ..., 10, 11, 12 cosi non puoi confondere 1 con 10.

Arrivato però al valore 10, composto da due cifre, 1 e 0, secondo me arduino lo legge come due numeri separati 1 e 0 e non 10,
come posso fare per ovviare questo problema?

C'è un buon motivo per usare i caratteri da 0-9?

Servono proprio i caratteri oppure può bastare inviare un byte raw e leggerlo raw, senza scomodare la codifica ASCII?

Se proprio devi usare 0-9 ASCII, quando superi il valore 9 potresti usare tante if (val0 == 1) && (val1 == 0), la seconda if (per 11) if (val0 == 1) && (val1 == 1), per 12 (val1 == 2). Tuttavia dovresti inviare sempre due caratteri anche nel caso il valore fosse minore di 10, cioè 01, 02, 03 ...

Ho come l'impressione che stai muovendo i primi passi con i protocolli di comunicazione, se è così, la tendenza è quella di studiare un protocollo che richieda meno codice, che semplifichi la codifica.

Nel caso in cui un byte raw può essere impiegato, questo permette 255 comandi, altri 255 possono essere ottenuti tramite una macchina a stati che decodifica i comandi in base allo stato attuale il quale può essere modificato da uno o più comandi ricevuti. Esempio la macchina a stati ha due stati 'comandi' e 'config', di default la macchina a stati si trova nello stato 'comandi', quando riceve 'y' lo stato muta in 'config' e qui puoi decodificare altri 255 comandi che nulla hanno a che fare con i comandi interpretati nello stato 'comandi'. Dal punto di vista dell'utente esso verrò informato che il controller può
essere impiegato nelle seguenti modalità: Manual Mode, Auto Mode, Settings Mode ecc, internamente per ogni modalità (stato) hai 255 comandi a disposizione.

Ciao.

Ciao Roberto e Mauro, vi ringrazio per la tempestiva risposta.

Il maggiore era un esempio, nel tuo caso è corretto il doppio uguale. Se vuoi usare più di 10 funzioni allora devi utilizzare 2 caratteri per ogni chiamata, quindi 01, 02, 03, ..., 10, 11, 12 cosi non puoi confondere 1 con 10.

Facendo così quindi arduino riconosce l'1 come 01 e il 10 come 1 e 0?
Grazie per la risposta.

Rispondendo alle domande di Mauro, no non è necessario per forza usare numeri da 0 a 9, sto interfacciando arduino con visual basic, quindi basta che sia un carattere (un numero dovrebbe essere più semplice).

Se proprio devi usare 0-9 ASCII, quando superi il valore 9 potresti usare tante if (val0 == 1) && (val1 == 0), la seconda if (per 11) if (val0 == 1) && (val1 == 1), per 12 (val1 == 2). Tuttavia dovresti inviare sempre due caratteri anche nel caso il valore fosse minore di 10, cioè 01, 02, 03 ..

Ma sei invio due caratteri, Arduino li riconosce o ne riconosce uno alla volta, impedendo cosi di usarne due insieme?

Riguardo a cosa hai citato in fondo al post, non ho capito molto.

Ti ringrazio per la tua risposta, in attesa
Buona giornata
Edo

Quella seguente è l'istruzione che usi per ricavare il byte ricevuto dalla seriale:

int val = Serial.read() - '0';

Come vedi, @val è una variabile di tipo 'int' (intero), quindi Serial.read() in realtà restituisce un valore numerico a cui tu sottrai il valore numerico corrispondente al carattere ASCII '0'.

Quindi togli - '0' e da visual basic trova il modo di inviare un byte raw (o binario) il cui valore può essere da te scelto tra 0-255 (quindi 256 valori).

Adesso al posto di usare tante if, usi il costrutto switch case:

switch (val) {
case 0:
    // inserisci il codice qui, esso verrà eseguito quando val == 0
    break;
case 1:
    // inserisci il codice qui, esso verrà eseguito quando val == 1
    break;
case 'A': // e uguale a scrivere "case 65:"
    // inserisci il codice qui, esso verrà eseguito quando val == 65
    // considera anche la possibilità di selezionare il case da eseguire al prossimo loop
    // esempio:
    val = 1; // al prossimo ciclo verrà eseguito il case 1.
    break;
default:
     // inserisci il codice qui, esso verrà eseguito quando val ha valore non previsto dai vari case
   
}

Nota che le possibilità sono infinite, ad esempio nel caso default: puoi inserire il codice che legge il carattere da seriale,
oppure cambiare il nome di val in currentState, inizializzandola nel setup e cambiandone il valore anche da dentro uno o più 'case n:'

Il case è utilizzato spesso per realizzare una macchina a stati finiti, ma ci sono anche altri metodi alcuni
più confusionari di switch case, altri più ordinati e completi pensati proprio per gestire una macchina a stati
con tanto di transizione tra stati e operazioni, permettendo di replicare il comportamento simile agli operatori
and e or tra stati, cioè esegui stato 1 and stato 2, oppure stato 1 or stato 2.

Ciao.

Ciao Mauro,
premetto che sono un neofita in arduino,

scusami ma non ho capito il codice che mi hai postato nell'ultimo post.

switch (val) {
case 0:
    // inserisci il codice qui, esso verrà eseguito quando val == 0
    break;
case 1:
    // inserisci il codice qui, esso verrà eseguito quando val == 1
    break;
case 'A': // e uguale a scrivere "case 65:"
    // inserisci il codice qui, esso verrà eseguito quando val == 65
    // considera anche la possibilità di selezionare il case da eseguire al prossimo loop
    // esempio:
    val = 1; // al prossimo ciclo verrà eseguito il case 1.
    break;
default:
     // inserisci il codice qui, esso verrà eseguito quando val ha valore non previsto dai vari case
   
}

Perchè hai messo questa parte

val = 1; // al prossimo ciclo verrà eseguito il case 1.

A cosa serve ?

Scusami ancora per il disturbo.
Buona serata
Edo

Credo sia solo un esempio, rimpiazzalo con quel che serve fare a te.

A cosa serve ?

Come dice @sukkopera, si tratta di un esempio che ti mostra come modificare lo stato da dentro uno stato. Spiegata così sembra poco comprensibile, ma se metti tutto assieme ti si apre davanti il fantastico mondo delle macchine a stati finiti ( FSM).

L'esempio vuole anche essere uno spunto valido in riferimento a quanto scritto, che riporto:

oppure cambiare il nome di val in currentState, inizializzandola nel setup e cambiandone il valore anche da dentro uno o più 'case n:'

Come ulteriore spunto, (e come esempio) in una macchina a stati, uno stato è equivalente ad uno specifico case n, ed è considerato specifico per volontà del programmatore, il quale lo identifica come stato, attribuendogli anche un univoco nome (anche composto).

Ad esempio dopo un reset hardware si dice che il microcontroller si trova nello stato di PowerOnResetState, tu secondo necessità puoi modellare più switch case partendo proprio da PowerOnResetState, eseguire codice ed imporre (o mettere fine allo stato corrente) specificando quale sarà il prossimo stato.

Per convenzione stabilisci che PowerOnResetState corrisponde il case 0: all'interno del quale scriverai codice da eseguire soltanto al PowerOnResetState, dopo devi decidere quale sarà il prossimo stato, e questo l'ho mostrato con quel codice che non hai compreso.

Tanta carne al fuoco può confondere oppure illuminare, esiste un modo per scrivere lo switch case maggiormente comprensibile, ma ciò comporta l'uso di due istruzioni C, typedef ed enum. Grazie a queste istruzioni un case n, si presenta così:

switch (FSMmain) {
    case FSMpowerOnReset: // grazie a typedef ed enum attribuisco un nome ad uno stato, si guadagna in comprensione.
    break;
}

Ciao.

Ciao,
vi ringrazio a tutti per le risposte
ho creato questo codice utilizzando lo switch case

void loop() {
while (Serial.available() == 0); // do nothing if nothing sent
int val = Serial.read() - '0'; // deduct ascii value of '0' to find numeric value of sent number

switch (val) {
    case (1): digitalWrite (ledPin,HIGH);
              Serial.println ("led1"); //led1
              break;
    case (2): digitalWrite (ledPin,HIGH);
              Serial.println ("led2"); // led2
              break;
    case (9+1): digitalWrite (ledPin,LOW);
              Serial.println ("led1"); //led1
              break;
    default: Serial.println ("non so che cosa");
             break;
  }
}

Però quando devo fare il case 10, non funziona perchè la seriale di arduino legge 1 e 0, ma non tutto il numero 10.

Mi scuso per il disturbo.
Saluti
Edo

Basta leggere due caratteri e convertirli in intero:

while (Serial.available() < 2)
  ; // do nothing if nothing sent

char n[3];
n[0] = Serial.read();
n[1] = Serial.read();
n[2] = '\0';
switch (atoi (n)) {
  // ...

Ma puoi semplificarti la vita con:

int val = Serial.parseInt();