leggere stringa da serial monitor

Ciao!
vorrei capire come devo fare per leggere ad esempio "on" e accendere un led.
per tanti è una sciocchezza, io invece sto imparando adesso...

ho fatto diversi tentativi ma non ho ottenuto ciò che mi aspettavo e mi sono solo confuso di più.
so che si usa Serial.read ma non capisco come. oltre tutto vorrei leggere una parola, come 'on' oppure 'acceso'.

qualcuno può spiegarmi come fare?
grazie per l'attenzione!
Luca

Ciao, io non sono un'esperto ma credo di poterti aiutare visto che sto "giocando" con questo argomento ultimamente. Allora, innanzitutto, devi stabilire i comandi, tieni a mente che la sintassi deve coincidere anche nelle minuscole/maiuscole, poi nella function loop() metti qualcosa come segue:

if (Serial.available() )
  { ser=Serial.read();
    if(ser=='on')
    {
      //<<<<qui devi mettere il tuo codice
    }
  
    else if (ser== 'off')
    {
      //<<<<qui devi mettere l'alternativa in caso di comando "off"
     }
    
  }
  
}

ovviamente, la variabile "ser" la dovrai dichiarare dove ti serve, global (in questo caso) o locale se sarà vista solo all'interno del loop, in questo caso, visto che servirebbe solo per sapere i comandi in arrivo, potrebbe benissimo essere locale.

spero ti essere stato d'aiuto e abbastanza chiaro.

ciao!

nel codice precedente, se vuoi aggiungere altri comandi, basterà aggiungerli nel ciclo if con un'altro (od altri) "else if" , ovviamente se prevedi diversi led che si possano accendere o spegnere, dovrai fare qualcosa del tipo --> 'Led1on" considera però la lunghezza della stringa, prende del tempo trasmetterle ed occupano anche più memoria (flash e di RAM), quindi se il tuo programma sarà molto lungo, tieni a mente ciò. In alternativa, potresti fare --> 1on, 1off, 2on, 2off ... o ancora più abbreviato 1n (n come on), 1f(f come off) cosy occupi solo 2 byte.

per il momento ti ringrazio! domani provo!
grazie ancora!
Luca

@hiperformance71

Non funziona così e lo sai o almeno se non faccio confusione dovresti saperlo.

Serial.read() legge un byte per volta, quindi la condizione if (ser == ... non sarà mai vera)
Anche perchè 'no' non è una rappresentazione asci di un byte e se usassimo i doppi apici "no" dovremo comparare un array di stringhe e non funziona
array di stringheA == arraydistringheB, c'è una funzione C standard per fare la comparazione, mi pare "cmp" e alcune varianti.

I comandi composti inviati via seriale devono avere un terminatore di comando: es
invio da pc x100/y50, vengono letti come 8 byte un byte per volta. "/" rappresenta il carattere sentinella (la scelta è arbitraria) che rappresente il terminatore di comando. Quindi leggo dalla seriale e metto in un buffer tutti i dati fino a che non incontro il carattere sentinella e quindi inzio a processare il comando, ricavando che si tratta di una coordinata x spostamento assoluto 100. Stessa cosa per tutti gli altri comandi.

Per iniziare conviene usare solo un carattere per comando e non usare i comandi composti, poi ci sono tanti modi di decodificare i dati in arrivo tramite seriale e genericamente l'uno dall'altro modo prende il nome di protocollo software.

Ciao.

Ciao.

Scusate, Mauro, hai ragione, ho fatto un errore, ho dimenticato quel particolare, in realtà il mio codice originale ricevendo il primo byte della seriale tramite un ciclo if..else if mi selezionava i comandi "base" poi vi era una chiamata ad un'altra function che si incaricava di

ho fatto una nuova correzione, l'ho collaudata e funziona, basta indicare prima il numero di led da accendere/spegnere cosi:

1on 1of 2on 2of 3on 3of questo è il codice:

void loop(){
  
if (Serial.available() )
  { ser=Serial.read();
    if(ser=='1')
    {
      Led_1();
    }
  
    else if (ser== '2')
    {
      Led_2();
      
    }
    else if (ser== '3')            //aggiungere qui altri comandi e chiamare le function corrispondenti, in questo caso, ho aggiunto un'utile chiamata a freeRam()
    {
      Led_3(); 
    } 
  }
  
}

//*****************************************************
  void Led_1(){  //legge 3 caratteri numerici ASCII sulla seriale, li converte a integer
    
    //Serial.println("OK, a premuto");
     
     delay(50);   ////<------------------questo delay è indispensabile!!
     if (Serial.available()) {
     
      a = Serial.read();                       
      b = Serial.read();                       
      
      Serial.println(a);                          //debug
      Serial.println(b);                          //debug
      
    }
    if (a=='o' && b=='n') { digitalWrite(LedPin1,HIGH); Serial.println("led 1 ON"); }
      else if (a=='o' && b=='f') { digitalWrite(LedPin1,LOW); Serial.println("led 1 OFF"); }

}
  //******************************************************
  void Led_2(){  //legge 3 caratteri numerici ASCII sulla seriale, li converte a integer
    
    //Serial.println("OK, a premuto");
     
     delay(50);   ////<------------------questo delay è indispensabile!!
     if (Serial.available()) {
     
      a = Serial.read();                       
      b = Serial.read();
            
      
      Serial.println(a);                          //debug
      Serial.println(b);                          //debug
      
    }
      if (a=='o' && b=='n') { digitalWrite(LedPin2,HIGH); Serial.println("led 2 ON"); }
      else if (a=='o' && b=='f') { digitalWrite(LedPin2,LOW); Serial.println("led 2 OFF"); }

}
  //*******************************************************
  void Led_3(){  //legge 3 caratteri numerici ASCII sulla seriale, li converte a integer
    
    //Serial.println("OK, a premuto");
     
     delay(50);   ////<------------------questo delay è indispensabile!!
     if (Serial.available()) {
     
      a = Serial.read();                       
      b = Serial.read();                       
      
      Serial.println(a);                          //debug
      Serial.println(b);                          //debug
      
    }
     if (a=='o' && b=='n') { digitalWrite(LedPin3,HIGH); Serial.println("led 3 ON"); }
      else if (a=='o' && b=='f') { digitalWrite(LedPin3,LOW); Serial.println("led 3 OFF"); }
}

Spero di non aver commesso altri errori, non voglio aggiungere confusione al nostro amico Luca con il quale mi scuso per il disguido di ieri sera (sarà stato la stanchezza :sleeping:) sperando che la risposta di oggi sia migliore a scapito della doppia stanchezza!! (non solo me ne sono andato a letto alle 2, ho preso sonno verso le 3 ed un rompi... di vicino si è messo a fare il fabbro a tagliare metallo con il Flex alle 5!! :0)

@Mauro,

è interessante ciò che dici anche se per ora risulta un po complicato da capire, dopotutto con il C/C++ ci sto lavorando da natale, ma anche io ho visto che la miglior forma di semplificare è avere quanti meno caratteri possibili per comando, infatti, sul primo approcio che ho fatto alla ricezione di comandi da pc, avevo assegnato ad ogni comando una unica (ed univoca) lettera, quindi avendo 3 comandi da impartire, avevo lettere dalla 'a' alla 'c' , ad ogn'una di esse corrispondeva una chiamata ad una function che seguiva a leggere il contenuto della seriale per stabilire il resto dei dati del comando, del tipo a1002 il comando era 'a' un ciclo if/else if selezionava quale carattere era arrivato per primo, quindi quale comando, poi rimandava ad una function unica che assegnava a diverse variabili il contenuto del buffer seriale, gli sottraeva 48 per essere sicuro che fossero numeri da 0 a 9 faceva un controllo di errore (se diverso dal range 0-9), e creava un numero intero da questi caratteri numerici ricevuti, così:

void loop(){
  
if (Serial.available() )
  { ser=Serial.read();
    if(ser=='a')
    {
      leggi();
    }
  
    else if (ser== 'b')
    {
      leggi_b();
      
    }
    else if (ser== 'c')            //aggiungere qui altri comandi e chiamare le function corrispondenti, in questo caso, ho aggiunto un'utile chiamata a freeRam()
    {
      Serial.println(freeRam()); 
    } 
  }
  
}

//*****************************************************
  void leggi(){  //legge 3 caratteri numerici ASCII sulla seriale, li converte a integer
    
    //Serial.println("OK, a premuto");
     
     delay(50);   ////<------------------questo delay è indispensabile!!
     if (Serial.available()) {
     
      a = Serial.read()-48;                       //viene sottratto 48 per rientrare nel range dei caratteri numerici ASCII (0=49,1=50....9=58)
      b = Serial.read()-48;                       //senza un sistema di check errore, se il sistema ricevesse una "a" per esempio,  
      c = Serial.read()-48;                       //verrebbe calcolato come "65"... x1000 o x100 o x10 o x1, quindi errato.
      d = Serial.read()-48;
      delayTime = (a*1000)+(b*100)+(c*10)+(d*1);  //se si deve ricevere 1, dalla seriale dovrà arrivare 0 0 0 1
      Serial.println(a);                          //debug
      Serial.println(b);                          //debug
      Serial.println(c);                          //debug
      Serial.println(d);                          //debug
      Serial.println(delayTime);                  //debug
    }

}
  //******************************************************
  void leggi_b(){  //questa function riceve un singolo carattere numerico, per esempio: 0=stop, 1=Forrard, 2=Reverse...
    
    //Serial.println("OK, b premuto");
    delay(5);
    if (Serial.available()) {
      x = Serial.read()-48;
      
      Serial.println(x);}
  
  }
  
  //*******************************************************
  int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 

}

questo qui sopra, in realtà fu un codice modificato che ho fatto per una risposta ad un post su comunicazione seriale ed era basato sull'approccio che avevo realizzato per me, dove vi erano appunto 15 comandi che mettevano arduino in attesa per i dati che ad ogni comando seguivano (dal pc invio dei dati che arduino converte a integer e poi memorizza in delle array specifiche per quel comando), il tutto mi funziona molto bene, ma ovviamente, per un'esperto magari ci sarebbero tante altre strade di ottenere gli stessi risultati senza creare un codice lunghissimo e pesante.

Praticamente lavorare spedendo un carattere come comando non è un problema in quanto il singolo carattere dal C viene visto come un byte ovvero come un numero.
Fare perciò un if tra numeri o if tra caratteri non cambia

int iVar;
char cVar;
...
if(iVar==65) ...
if(iChar=='A') ...

per il compilatore il secondo if viene visto come if(iChar==65) perchè ascii di A=65 (tabella ascii)

Moooolto diverso è lavorare con le stringhe (vettori di caratteri) in quanto vettori (array) che possono anche essere visti come puntatori (ancora peggio).
Lavorare poi con l'oggetto String è ancora diverso, in quanto è un oggetto complesso, nel senso che "contiene" al suo interno i dati; non è una semplice variabile int o char.

void leggi(){  //legge 3 caratteri numerici ASCII sulla seriale, li converte a integer
     delay(50);   ////<------------------questo delay è indispensabile!!
     if (Serial.available()) 
     { a = Serial.read()-48;                       //viene sottratto 48 per rientrare nel range dei caratteri numerici ASCII (0=49,1=50....9=58)
       b = Serial.read()-48;                       //senza un sistema di check errore, se il sistema ricevesse una "a" per esempio,  
       c = Serial.read()-48;                       //verrebbe calcolato come "65"... x1000 o x100 o x10 o x1, quindi errato.
       d = Serial.read()-48;

Il pezzo di codice che hai postato non è male ed eviterebbe la atoi. Ma secondo me ha un problema di base, da per scontato che arrivino sempre 4 cifre.
Quel pezzo di codice legge il carattere numerico, esempio '2' che equivale a 48='0';49='1';50='2' perciò 50; 50-48->2
Un metodo matematico semplice per convertire '2' (ovvero 50) in 2 come cifra.

spero di averti spiegato i pezzi di codici che hai postato.

nid69ita:

void leggi(){  //legge 3 caratteri numerici ASCII sulla seriale, li converte a integer

delay(50);   ////<------------------questo delay è indispensabile!!
    if (Serial.available())
    { a = Serial.read()-48;                       //viene sottratto 48 per rientrare nel range dei caratteri numerici ASCII (0=49,1=50....9=58)
      b = Serial.read()-48;                       //senza un sistema di check errore, se il sistema ricevesse una "a" per esempio,  
      c = Serial.read()-48;                       //verrebbe calcolato come "65"... x1000 o x100 o x10 o x1, quindi errato.
      d = Serial.read()-48;



Il pezzo di codice che hai postato non è male ed eviterebbe la atoi. Ma secondo me ha un problema di base, da per scontato che arrivino sempre 4 cifre. 
Quel pezzo di codice legge il carattere numerico, esempio '2' che equivale a 48='0';49='1';50='2' perciò 50; 50-48->2
Un metodo matematico semplice per convertire '2' (ovvero 50) in 2 come cifra.

spero di averti spiegato i pezzi di codici che hai postato.

Si, effettivamente fatto così, devo per forza struturare i dati in modo che sappia sempre il numero di dati in arrivo, in realtà l'avevo creato per provare a creare un mio protocollo di comunicazione tra pc e arduino, come base funziona abbastanza bene, visto che i dati sono organizzati in base al carattere "comando" che arriva per primo, i dati numerici che arrivano in seguito devono avere il formato stabilito (ma del resto i protocolli come funzionano? se prendiamo il CAN per esempio, ha una struttura ben definita da standard, i singoli sviluppatori decidono in che ordine e con che metodo organizzare i dati trasmessi, ad esempio, giri motore, MAP, TPS, TempMotore ecc)
io ho tentato di crearmi un mio "protocollo" consapevole che è ben lontano dall'essere perfetto e a prova di errori, ma essendo che sto imparando, e per ora, i risultati ottenuti sono buoni, ho ridotto ad un minimo il numero di function predisposte alla ricezzione dei dati in arrivo, per esempio, ci sono comandi che implicano la ricezzione di 8 numeri composti da 3 cifre, quindi viene chiamata due volte la routine che "legge" le prime 4 cifre in modo da creare 8 numeri, spiegarlo a parole è difficile ma credo abbia reso l'idea. Con questo metodo, riesco a programmare una serie di array diverse costituite da 1, 2, 4, 8 numeri a 3 cifre tutto in un unico "colpo", del tipo: a100200300400b120130140150160c001002003004... l'idea è questi dati siano inviati da un programma su pc dove questi comandi siano ben stabiliti con i dati da trasmettere, ovviamente trasmettiamo stringhe troppo lunghe, prima o poi avvengono degli errori, ma ho notato che immettendo un delay di 50ms il problema non si presenta con un'invio di oltre 40 byte, ovviamente, l'idea è di non superare una soglia considerata affidabile, per esempio, le linee CAN possono avere diverse lunghezze in base alla specifica versione fino a 29 bytes (la 2.0B se non ricordo male). Come detto, so che esistoni metodi migliori, ma io sto agli inizi, e mi baso più sulla pratica che sulla teoria, per ora questa è ancora piena di lacune, sto provvedendo, ma sono lento ad imparare, purtroppo oltre ad altri problemini il tempo a disposizione è pochino.

in giro trovo tantissimi esempi di protocolli seriali per comandare a distanza arduino, ma anche li, esperti e meno esperti, ogn'uno ci mette del suo, non è che abbia trovato un' unica strada per farlo, alcuni sono semplici come il mio (l'ho imparato da un esempio) ed altri dove serve indubbiamente tanta esperienza di C per capirci qualcosa, per ora, non è il mio caso...

Ciao e grazie per le critiche costruttive! :wink:

Tanto per parlare. La decodifica che vediamo nel codice è di tipo strimming, cioè i dati vengono decodificati e usati e i dati di partenza si buttano. Altre codifiche potrebbero essere il parser a pacchetto, un pacchetto viene spedito (client), chi è in ricezione legge inizio pacchetto e fine pacchetto, terminata questa fase avvia il parser dei dati.

Il pacchetto dovrebbe contenere il numero minimo di dati per considerare il pacchetto coerente e funzionante, questo è importate quando la storia dei dati è fondamentale, tradotto il dato che sto analizzando assume un significato particolare determinato dal dato precedente e così via.

Su PC i programmi usano dialogare con un IPC, si tratta di un protocollo dati per far si che le applicazioni si scambino informazioni, uno di questi è DBus, strano che ancora non ci sia stato qualcuno che ha creato un DBus con risorse minime.

Questo DBus dovrebbe essere in grado di ricostruire un stringa da una serie di dati inviati via seriale, o un numero, intero con o senza segno ecc. Questo non è molto efficiente ma è flessibile, cioè si perde velocità perchè oltre al dato devi inviare il tipo.

@hiperformance71
Il tuo obbiettivo è quello di crearti il protocollo rigido e funzionante, poi se hai tempo e vuoi renderlo flessibile ci lavori, ma nel mentre il tuo progetto sta lavorando, diversamente saresti ancora li a chiederti; mi serve davvero inviare un float?
Quindi secondo me hai agito bene dritto all'obbiettivo, diverso potrebbe essere se programmassi sul pc, li non hai scuse di risorse limitate e c'è spazio e per curare la flessibilità.

Ciao.

caricare il tutto su una stringhetta e lavorarla non vi piace?

String inputString = ""; 
  inputString.reserve(100);
  
  while (Serial.available())  {
    char c = (char)Serial.read(); 
    inputString += c;
    
    if (c == '\n') {
        //----------sringa su cui lavorare-----------
        Serial.println(inputString); 
        //--------------------------------------------
        inputString = "";
    } 
  }

caricare il tutto su una stringhetta e lavorarla non vi piace?

Si mi piace. Però dipende se conviene, cioè non ho a vista il codice di String, se il ridimensionamente è gestito in modo efficiente potrebbe essere l'ideale per il parser a pacchetto.

If ()
tutto dentro String fino a che non incontro
If (String non è vuota)
parser(String *ptr) // non vogliamo passare un stringa troppo oneroso, meglio un puntatore a String

Ciao.

MauroTec:

caricare il tutto su una stringhetta e lavorarla non vi piace?

Si mi piace. Però dipende se conviene, cioè non ho a vista il codice di String, se il ridimensionamente è gestito in modo efficiente potrebbe essere l'ideale per il parser a pacchetto.

Visti tutti i problemi di saturazione della RAM che si hanno con l'oggetto String, non credo che esso gestisca in modo "efficiente" la memoria :wink:

leo72:

MauroTec:

caricare il tutto su una stringhetta e lavorarla non vi piace?

Si mi piace. Però dipende se conviene, cioè non ho a vista il codice di String, se il ridimensionamente è gestito in modo efficiente potrebbe essere l'ideale per il parser a pacchetto.

Visti tutti i problemi di saturazione della RAM che si hanno con l'oggetto String, non credo che esso gestisca in modo "efficiente" la memoria :wink:

Come detto più volte nei miei post, io sono un profano in materia, voi ne sapete MOOOOLTO più di me al riguardo, ma nelle mie sperimentazioni, ho costatato che l'uso di string è una "mazzata" per il consumo di RAM e FLASH, quindi avendolo anche letto in giro, ho preferito il mio approccio "grezzo" più che altro perché devo adeguare il codice alle mie capacità (un po scarse per il momento!) quindi non posso prendere un codice bello e pronto, magari super ottimizzato se non sono in grado di capirlo, altrimenti rischio che se devo apportare modifiche dopo qualche mese o due, non ci capisco un bel niente (ah, la mia memoria!) non è che cambi tantissimo fare il codice a modo mio, ma qualcosa in più ci capirei (almeno spero!).

Per esempio, l'idea di usare le string proposta da Pablos la capisco appena, se ho capito bene, tutto quello che viene sul buffer seriale viene accodato in una string da 100 caratteri giusto? sarebbe tipo un'array ma di string? ma poi, come si estraggono i dati da essa all'interno, come farei con un' array immagino? ovviamente a questo punto dovrei conoscere l'index o qualche char identificativo o separatore no? e poi dovrei convertirlo a int visto che quasi sempre sono dati numerici quelli che mi servono. MMM... C'è tanto da imparare...

grazie per tutte le risposte!
purtroppo faccio un po' fatica a capire tutto quello che avete scritto. siete decisamente più avanti rispetto a me.
oggi ho un po' di tempo e proverò...
credevo che leggere una "parola" fosse molto più semplice e invece devo dire che(per me) non lo è...
anche io sono alle prime armi e ho bisogno di molto studio per arrivare alle vostre conoscenze.
detto ciò vi ringrazio moltissimo per l'aiuto.

proverò i codici e se avrò bisogno riscriverò!

Luca

Ciao Luca, ricordati di prendere ad esempio il secondo codice che ho postato!! (ma anche il terzo se preferisci, sono entrambi funzionanti). :wink:

sarà fatto! grazie ancora

hiperformance71:
Per esempio, l'idea di usare le string proposta da Pablos la capisco appena, se ho capito bene, tutto quello che viene sul buffer seriale viene accodato in una string da 100 caratteri giusto? sarebbe tipo un'array ma di string? ma poi, come si estraggono i dati da essa all'interno, come farei con un' array immagino? ovviamente a questo punto dovrei conoscere l'index o qualche char identificativo o separatore no? e poi dovrei convertirlo a int visto che quasi sempre sono dati numerici quelli che mi servono. MMM... C'è tanto da imparare...

Se si usassero linguaggi tipo Pascal o Basic (naturalmente su PC senza problemi di memoria) le stringhe sarebbero più semplici perchè gestite "di nascosto". In C invece il linguaggio è mantenuto grezzo perchè NON deve implementare gestioni speciali un pò come se lavorassimo in assembler. Perciò si lavora con gli array (vettori) di char (che poi il compilatore vede come puntatori) terminati semplicemente da '\0' o null.
Cioè il C nasce come semplificazione dell'assembler ma l'idea era un linguaggio senza gestioni specializzate come le strighe in basic o pascal.
Il turbo pascal di una volta, ad esempio, gestiva le stringhe (senza farlo sapere al programmatore) come vettori di 256 caratteri, dove il primo elemento era la lunghezza dei caratteri validi. Le funzioni sulle stringhe sapevano perciò di dover rispettare questa strutturazione. Si potrebbe implementare anche in C.
In C++ e quindi in Arduino (ma anche Java) è stato implementato invece l'oggetto String, che cerca di "nascondere" come è fatta/gestita una stringa.
In questo caso chi ha creato questa classe ha stabilito i nomi dei metodi ovvero dei comandi sulla stringa.
Purtroppo il C++ e Java danno per scontato che nel programma venga "aggiunto" un sistema di "pulizia" della RAM (garbage collection). Cosa che in Arduino non credo ci sia.

Spero di non aver aggiunto confusione e di non aver detto strunz@te.

Ciao a tutti. Questo è il mio primo post.
Rievoco questo in quanto mi pare di aver trovato una soluzione a come leggere un numero dal serial monitor.
Questa funzione restituisce in num, il numero in decimale letto dal seriale.
Non ho fatto molti test, ma è già un inizio. Che ne pensate?

int SerialReadNum(){
  byte reading[4]; //conterrà il numero letto dal seriale - dimensione 5 per contenere il maxINT
  int i=0, j=0; 
  int num=0; //valore restituito
  
  //leggo dalla seriale tutto il testo inserito
  while(Serial.available()){
    reading[i] = Serial.read())-48; //sottraggo 48 per converire da ASCII a numero
    i++;
   }
  i--;
  j=i;
  i=0;
  //converto le cifre in reading[] in notazione decimale
  while (j>=0){
   num = num + reading[j]*pow(10,i);
   i++;
   j--;
  }
  return num;
}

Ciao a tutti.