Comunicazione seriale

Utilizzando la porta seriale con il c# e arduino mi è sorto un dubbio,se volessi indirizzare due valori a due variabili diverse.... tipo
"A123" per a = 123
"B235" per b = 235
... come faccio ? ... ho provato in questo modo...

if (Serial.available()>0 )
{
if (Serial.read() == 'b')
{
x = Serial.parseInt();
Serial.flush();
Serial.println("b");
}
else if (Serial.read() == 'c')
{
delayTime = Serial.parseInt();
Serial.flush();
Serial.println("c");
}

Ho cercato di farlo in questo modo, con la b funziona tutto, quando metto un valore tipo c125 non succede niente (non mi stampa nemmeno la c sul seriale)... come mai ?

Ciao, io non sono un'esperto anzi sono alle prime armi con arduino e la sua programmazione, ma credo di avere un suggerimento al riguardo perchè l'ho sperimentato qualche settimana fa e funziona bene:

void loop(){
  
 if (Serial.available()) {

    ser = Serial.read();   //ser è la variabile che raccoglie il contenuto del buffer seriale in ingresso dell'arduino.

   if(ser == 'a'){
      Trasmissione_Dati_a_PC();     //<----trasmette un dato al pc... o qualsiasi valore letto sulle porte ADC/digitali per esempio... 
   }
   
   else if(ser == 'c'){
           Rx_New_Data_da_PC();        //<----riceve i dati trasmessi dal pc da memorizzare nellèarray... 
   }
   
   }

poi, in una function chiamata Rx_New_Data_da_PC() inserisco il seguente codice:

void Rx_New_Data_da_PC){   //questa function riceve dei dati ... poi modifica l'array con i nuovi valori...  

Azzera_Valori();   //function dove vengono azzerati i vari contatori e/o variabili global usate.
Rx_Data();          //function che receve i dati...
     Value1 = Value;
Rx_Data();
    Value2 = Value;
Rx_Data();
    Value3 = Value;
Rx_Data();
    Value4 = Value;
  
//Errore_RX è un contatore di errore, se i dati ricevuti non sono compresi da byte da 0 a 9 ovvero riceve "m" invece di "1" viene 
//incrementato il contatore da 0 (nessun errore) a 1 e il seguente if provvede a inmettere dei valori di default, ma l'uso che se
//ne può fare dipende dalla singola applicazione...
  
  if (Errore_Rx==1) {(Value1 = Linearizzazione_Sensore[0]); (Value2=Linearizzazione_Sensore[1]);    
                     (Value3= Linearizzazione_Sensore[2]); (Value4=Linearizzazione_Sensore[3]);}
  
  
 Linearizzazione_Sensore[0] = Value1 ;  //modifica l'array con il nuovo dato...
 Linearizzazione_Sensore[1] = Value2;
 Linearizzazione_Sensore[2] = Value3;
 Linearizzazione_Sensore[3] = Value4;
}

e questa la function che ci consente di prendere ogni valore arrivato nel buffer:

void Rx_Data(){
  
  delay(50);   ////<------------------questo delay è indispensabile!!
 if (Serial.available()) {

    Ser2 = Serial.read();                                           //legge il primo byte arrivato dopo in byte "comando"
    if (Ser2<48 || Ser2>57) (Errore_Ricezione() );        //una sorta di check, se non incluso nei caratteri 0@9 da errore.
      
    Ser3 = Serial.read();                                           //secondo byte ricevuto,
    if (Ser3<48 || Ser3>57) ( Errore_Ricezione() );
      
    Ser4 = Serial.read();                                           //terzo byte ricevuto
    if (Ser4<48 || Ser4>57)  (Errore_Ricezione() );
    
    Ser5 = Serial.read();                                            //quinto byte ricevuto (una virgola ma per uso separatore)
    if (Ser5!=',') (Errore_Ricezione() ); 
    
    Ser2 = Ser2 - 48;
    Ser3 = Ser3 - 48;
    Ser4 = Ser4 - 48;
    
    Value = ((Ser2 * 100) + (Ser3 * 10) + (Ser4 * 1) );   //questa semplice formuletta trasformerà i singoli caratteri 
                                                                         //numerici in una cifra int che poi potrà essere trattata nei 
                                                                          //calcoli dell'arduino se necessari...
 }
}

ovviamente, le variabili sono global, se sai come passarle tra funzioni potresti fare di meglio, a me va bene così per ora. Spero di essere stato d'aiuto.

Ciao,
Antonio

haha... non ci ho capito un *******..

  1. Trasmissione_Dati_a_PC(); non mi serve a niente visto che non devo trasmettere niente...devo solo ricevere...
    2)Errore_Ricezione() altrettanto visto che sono io (da un programma in c#) ad inviare dati [ad esempio Serialport1.write("A1234")]
    3)Azzera_Valori(); che è ?
  2. if (Errore_Rx==1) idem
    5)se i caratteri sono piu di 3 ???
    6)alla fine Rx_New_Data_da_PC(); non serve a niente visto che quello che fa tutto è Rx_Data(); ....
  3. che cacchio è la ","
    scusa ... ma secondo me per quello che devo fare non serve tutto questo "casino"...

niente parolacce, grazie

Scusa, ma quello che ti ho postato è un esempio di quello che ho fatto io, ovviamente, tu devi adattarti il programma alle tue necessità! quelle che non ti serve lo togli, quello che ti manca ce lo metti, e così via!

tutte quelle cose a me mi servono per creare una comunicazione bidirezionale con il pc, il pc manda delle richieste, arduino esegue. se a te serve solo che riceva, semplicemente usa la parte ricezione. i controlli di errore mi servono per essere sicuro di non ricevere GARBAGES dalla seriale, se ricevesse un b1w0 invece di un b130 e con il primo l'arduino non eseguirebbe nulla, perchè non lo riconosce, se invece non vi è un minimo di controllo errori, quella "w" potrebbe incasinare (dicasi bloccare) l'esecuzione o no? AH, giusto per chiarezza, i dati ricevuti dalla seriale sono byte, quindi ricevere lettere significa avere dei valori della tabella ASCII, dove a = "65" per esempio... Ma se a te non serve, non lo usare!
idem per la function che azzera i contatori, a me, particolarmente, serve.

il codice da me postato fa parte di uno che è almeno 50 volte più grande e che funziona benissimo, riconosce gli errori, esegue solo i comandi riconosciuti e posso inviare dal pc anche una sequenza di comandi del tipo:

b100,100,130,150,c140,150,160,170, .... nel mio caso, ogni trasmissione comando implica che il pc invia 4 blocchi di 4 numeri di tre cifre in formato ASCII separati da virgola(ma se ne può fare a meno), la lettera all'inizio di ogni blocco è il comando.

mi dispiace di averti fatto perdere del tempo a leggere il mio codice, forse avevi ragione, di sicuro non c'e bisogno di tutto sto casino di programma, magari il tuo fa solo un paio di cose semplici, il mio magari è molto più complicato ed io l'ho complicato ulteriormente...

Ovviamente se sai programmare bene (io no, sto imparando) magari trovi altre 100 forme migliori di fare la ricezione dei comandi, nel mio caso, al ricevere un comando da seriale, il ciclo if..else if mi seleziona il primo carattere arrivato (il comando), poi lo indirizza alla function corrispondente (per facilitare il mio compito) ma per evitare di ripetere le stesse istruzioni della function ->RX_Data() per quanti comandi ne ho, quindi allungare il programma di molto sia di lunghezza in linee scritte che in bytes occupati in compilazione, ho "meccanizzato" una parte delle operazioni ripetitive, il mio programma ha comandi dalla "a" alla "o" (15 comandi) per ora e quasi tutti in ricezione, e non sono nemmeno ad 1/4 del totale, capirai che ci vuole un certo ordine. Come detto, magari un esperto lo farebbe in 4 righe, io non sono all' altezza per il momento.

ciao.

ok, ho provato a fare qualche modifica....

if (Serial.available()>0 )
{
if(Serial.read() =='a')
{
delay(50);
a = Serial.read()-48;
b = Serial.read()-48;
c = Serial.read()-48;
d = Serial.read()-48;
delayTime = (a1000)+(b100)+(c10)+(d1);
Serial.println(a);
Serial.println(b);
Serial.println(c);
Serial.println(d);
Serial.println(delayTime);

}
else if (Serial.read() == 'b')
{
x = 1;
Serial.println(x);
}

in effetti la variabile delayTime viene cambiata ...ad esempio se scrivo a0030 allora delayTime = 30, il problema arriva con il comando b... x non diventa 1 e non stampa nemmeno il suo valore...(questo accade anche togliendo l'esle)...
Giusto per informazione sto facendo un progetto in cui cambio orientamento e velocità di un motore passo passo, il delayTime è la velocità del motore ( tempo tra un passo[scatto] e l'altro) e la x mi decide la rotazione [x =0 fermo, x =1 orario, x = 2 antior]...come posso risolvere ?

ho di menticato di mettere il delay(50); nell'istruzione Serial.read == 'b'...ma il problema persiste..

ok, ho giocato un po con il tuo codice (bravo per la compattazione!) ed ho trovato il problema:

if (Serial.available() )
  { ser=Serial.read();
    if(ser=='a')
    {
      delay(50);
      a = Serial.read()-48;
      b = Serial.read()-48;
      c = Serial.read()-48;
      d = Serial.read()-48;
      delayTime = (a*1000)+(b*100)+(c*10)+(d*1);
      Serial.println(a);
      Serial.println(b);
      Serial.println(c);
      Serial.println(d);
      Serial.println(delayTime);
      delay(50);
    }
  
    else if (ser== 'b')
    {
      x = 1;
      Serial.println(x);
    }
    
  }

semplicemente, ho inserito una variabile "var" la quale viene assegnato il valore in entrata su Serial.read(), semplicemente, dovevi eliminare la seconda chiamata a Serial.read perché già all'interno del ciclo if Serial.available() l'ho collaudata e funziona. spero che ti sia d'aiuto.
ciao.

il delay(50), l'ho aggiunto alla fine del blocco, infatti, se riceviamo una stringa molto lunga dal pc, si rischia di perdere bytes, adesso, puoi inviare ad arduino anche un serie di comandi del tipo:

a1200ba1250ba1400b.... ovvero, puoi concatenare una serie di comandi, ma per quello che devi fare, con b, se fatto così come da esempio, dovrai usare un contatore per poter mettere arduino in uno dei 3 stati previsti, giusto? o invierai il comando: b0, b1, b2? forse la soluzione migliore o a tutt' al più, potresti fare 3 comandi, s=stop, f=forward, r=reverse. non sarebbe anch'esso utilizzabile? da una parte complica ma dall'altra rende il protocollo più immune ad eventuali errori di ricezione perchè dovrebbe passare attraverso il filtro del comandi singoli. E per questo che nel mio shetch ho previsto il controllo degli errori, per ora è del tutto grossolano, se non sono numeri non fa nulla, ma nella versione definitiva dovrà contenere un sistema di checksum, le operazioni da eseguire sono alquanto critiche e devo essere sicuro che i dati ricevuti o trasmessi siano integri, altrimenti, succedono casini, anche gravi. Nel tuo caso, se devi fargli fare solo questo senza che il motore faccia muovere nulla che possa fare danni se sbaglia comando, allora procedi come pianificato, perchè come dicono spesso gli americani ed gli informatici, K.I.S.S. (Keep It Simple, Stupid)http://it.wikipedia.org/wiki/KISS_(informatica)

Ok, grazie, funziona benissimo :slight_smile:

si, in effetti usero b0 = stop b1= orario e b2 = antior. .... in pratica devo creare una specie di "stampante 2d" con 2 motori passo passo che muovono una penna sul foglio (tipo questa Arduino plotter xy tafel versie 2 - YouTube).... nelle linee rette il problema del delay(50) non si pone ... visto che dall'inizio alla fine della linea le velocità rimarranno fisse, ma quando inizierò a creare funzioni per fare circonferenze ... allora avrò qualche problema perchè la velocità dei motori cambierà molto frequente mente ( quasi 0.001 s)... secondo te in quella situazione come potrei fare ?

ok, adesso mi è più chiaro. darti una risposta univoca non posso, non ho mai "giocato" abbastanza con i passo-passo, ma se richiedi un'aggiornamento ogni 1 ms, credo che farlo in real time da seriale sarà un problema secondo me, magari qualcuno esperto ti potrà essere più d'aiuto, tieni presente la velocità di trasmissione della seriale, per esempio, la più veloce che potresti usare è 115200 bit per secondo ovvero 115.2 bit per ms. il delay(50) potresti provare a ridurlo o eliminarlo e vedere come va, a me, andava bene fin quando non mandavo catene di comandi e dati lunghe, per quelle da singolo comando anche senza ricordo che mi funzionava (da premettere che ho fatto prove solo a 9600 bps quindi molto al di sotto del massimo possibile). Quindi prova a vedere se non ti perde comandi/passi togliendo del tutto i delay(50), se invece succede, prova ad aumentare gradualmente, del tipo delay(5)...10, 15, 20...

ho testato un po il tuo codice a 115200 bps inviando una catena casuale di almeno 40 numeri e sembra ok ma senza un sistema di controllo errori non so se davvero è ok.

ho testato il mio codice a 115200, senza delay o con delay fino a 5ms, se provavo a trasmettere molti dati, ottenevo errore, solo se inviavo un singolo carattere "a" anche in una catena di 60 contemporaneamente, ottengo 60 risposte positive da arduino. quindi come vedi, qui dovrai lavorarci un po su e provare diverse soluzioni.

Dovresti studiare qualche sketch di quelli che si sono creati una CNC con arduino e capire come hanno fatto.

ok, cmq la soluzione migliore sarebbe gestire le linee dal programma c# e le circonferenze direttamente dall'arduino ricevendo da serial solo il centro ed il raggio....di spiare da altri programmi simili non ne ho molta voglia, visto che mi piace sperimentare ( e sbagliare) per apprendere meglio :slight_smile:

stavo per consigliarti propio quella strada, o inviare i dati e poi arduino li esegue in autonomo o gli invii solo i dati base, poi, con dei calcoli interni si incaricherebbe del pilotaggio dei motori. intuisco come potrei fare ma non so ancora come lo farei se fossi al tuo posto.
Beh, non credo che andare a vedere il codice di uno sketch sia una cosa grave, dopotutto lo spirito dell'open source è proprio questo, condividere le proprie conoscenze e/o lavori per puro divertimento o spirito di partecipazione o aiuto al prossimo. Ovviamente se ti senti meglio facendo tutto da te, sei libero di fare come credi meglio, avvolte anche io preferisco la strada più lunga e laboriosa, perche alla fine, qualsiasi risultato ottengo, buono o meno che sia, sarà stato frutto del mio impegno ed ingegno. Questo però non toglie che vedere come qualcun'altro ha affrontato una cosa simile, sia sbagliato, ovviamente a me non piace copiarlo con taglia ed incolla e via, voglio metterci del mio, adattarlo alle mie necessità o meglio, alle mie capacità. Per esempio, esistono un sacco di librerie, farebbero la felicità di chiunque, ma essendo che ancora non domino bene la programmazione e preferisco andare per gradi, mi sembra più complicato tentare di far funzionare una libreria fatta da un professionista e che per tanto, difficilmente io comprenderò a fondo in ogni sfumatura e quindi preferisco di creare una mia versione, a modo mio, magari poi, dopo un po, la rivedo e la miglioro.
Comunque buona fortuna, fai tante prove pratiche.

Ciao :wink:

cmq visto che hai detto che stai ancora imparando ... ti puo essere utile questi video.. in pratica ti spiega come fare videogiochi in 2d partendo da 0... è in inglese ma tutto sommato si capisce, io per creare le funzioni di linee e circonferenze mi sono ispirato a questo, magari può esserti utile anche a te.

cmq.... a chi interessa , ho modificato il codice per avere lapossibilità di mettere più comandi in una sola linea di codice utilizzando il metodo che credo si chiama "ricorsivo"[chiamare la funzione nel codice della stessa funzione (in pratica chiamare se stessi)]

void leggi()
{
if (Serial.available() )
{
ser=Serial.read();
if(ser=='a')
{
delay(50);
a = Serial.read()-48;
b = Serial.read()-48;
c = Serial.read()-48;
d = Serial.read();

delayTime = (a100)+(b10)+(c*1);
Serial.println(a);
Serial.println(b);
Serial.println(c);

Serial.println(delayTime);
delay(50);
if (d == ' ')
{
leggi(); // ricorsione
}
}
else if (ser == 'b')
{
delay(50);
f = Serial.read()-48;
d = Serial.read();
x = f;
Serial.println(x);
Serial.flush();
if (d == ' ')
{
leggi(); // ricorsione
}

}

}

in questo modo possiamo scrivere piu comandi nella stessa riga separandoli con uno " "(spazio) ... chiaramente chiameremo la funzione leggi(); nel void loop.

Occhio che la ricorsione brucia risorse in fretta.
Rischi di bloccare il micro se gli arriva una serie molto lunga di comandi.

molto lungha quanto ? (a me servono 20 caratteri....)

barlettaag:
molto lungha quanto ? (a me servono 20 caratteri....)

Dipende da quante risorse occupa il tuo sketch nel globale. Lo stack che serve a gestire i salti ricorsivi vive nella memoria RAM, gli stessi 2048 byte che contengono tutte le variabili del tuo programma ed i buffer seriali.

Interessante, ma effettivamente, chiamare se stesso troppe volte potrebbe riempire la RAM disponobile se il numero di chiamate è elevato o se si sta già a corto di RAM, nei picaxe vi era un limite al numero di gosub utilizzabili altrimenti si bloccavano, immagino sia quasi la stessa cosa.

io invece lo avevo risolto creando una function ->Rx_Data() che ha una struttura pensata in base alla ricezione di di 3 cifre (char numerico ASCII) ogn' una, questa function assegna alla variabile ->Value il valore ricevuto, il programma da cui ti ho mandato degli spezzoni, consta (per ora) di 15 comandi diversi, alcuni ricevono solo 1 numero compreso tra 000 e 999, altri ne hanno 4 numeri da 000 a 999, altri ne hanno 8 numeri da 000 a 999 ed altri ne avranno fino a 256 (una matrice di 16x16) sempre da 000 a 999 (quest'ultima parte no l'ho ancora realizzata). E per questo che ho quel grado di complessità, come detto nei post precedenti, magari un programmatore esperto lo farebbe molto meglio di me (io sto imparando!), per me sta bene così (per ora), magari non è perfetto, ma funziona e ci capisco tutto, così in caso di ampliamenti o modifiche future, ci capirò.

ciao.

PS. attualmente questo mio programma (non completo) occupa 700 linee di codice, compilato occupa 9700 bytes (tanti, devo "tagliare" o rischio che il resto non mi entra) e mi tiene liberi 900 bytes di RAM (troppi pochi considerando tutto quello che ci manca!) ma tieni presente che uso solo variabili global e qualche local, e un totale di 400 bytes in arrays varie, tra cui, una da 16x16. Man mano imparerò di più, il prodotto finito sarà più efficiente e compatto.