Go Down

Topic: leggere stringa da serial monitor (Read 24890 times) previous topic - next topic

lucadh

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:

Code: [Select]

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!
"The only way to do great work is to love what you do. If you haven't found it yet, keep looking. Don't settle" Steve Jobs

"Se gommo tiene, io vince gara. Se gommo non tiene, io come bomba dentro montagna." Markku Alen

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.
"The only way to do great work is to love what you do. If you haven't found it yet, keep looking. Don't settle" Steve Jobs

"Se gommo tiene, io vince gara. Se gommo non tiene, io come bomba dentro montagna." Markku Alen

lucadh

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

Maurotec

@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:

Code: [Select]

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 :smiley-sleep:) 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)




"The only way to do great work is to love what you do. If you haven't found it yet, keep looking. Don't settle" Steve Jobs

"Se gommo tiene, io vince gara. Se gommo non tiene, io come bomba dentro montagna." Markku Alen

@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ì:

Code: [Select]

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.

"The only way to do great work is to love what you do. If you haven't found it yet, keep looking. Don't settle" Steve Jobs

"Se gommo tiene, io vince gara. Se gommo non tiene, io come bomba dentro montagna." Markku Alen

nid69ita

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
Code: [Select]

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.
my name is IGOR, not AIGOR

nid69ita

Code: [Select]

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.
my name is IGOR, not AIGOR

#9
Mar 23, 2013, 11:53 pm Last Edit: Mar 24, 2013, 12:06 am by hiperformance71 Reason: 1

Code: [Select]

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! ;) 
"The only way to do great work is to love what you do. If you haven't found it yet, keep looking. Don't settle" Steve Jobs

"Se gommo tiene, io vince gara. Se gommo non tiene, io come bomba dentro montagna." Markku Alen

Maurotec

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.


pablos71

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

Code: [Select]
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 = "";
    }
  }
L'esperienza è il tipo di insegnante più difficile ....
Prima ti fa l'esame e poi ti spiega la lezione.

Maurotec

Quote
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 (<Inizio pacchetto>)
    tutto dentro String fino a che non incontro <fine pacchetto>
If (String non è vuota)
    parser(String *ptr)  // non vogliamo passare un stringa troppo oneroso, meglio un puntatore a String

Ciao.

leo72


Quote
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  ;)



Quote
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  ;)


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...
"The only way to do great work is to love what you do. If you haven't found it yet, keep looking. Don't settle" Steve Jobs

"Se gommo tiene, io vince gara. Se gommo non tiene, io come bomba dentro montagna." Markku Alen

Go Up