Separazione dati da seriale bluetooth

Buongiorno a tutti, sto lavorando ad un piccolo progettino di una macchina radiocomandata da due joystick (uno sterzo uno accellratore) tramite bluetooth con due hc-05 e mi sono bloccato sulla ricezione dei comandi.
In parole povere leggo i due potenziometri separatamente r li invio sulla seriale bluetooth, dal lato della ricevente invece metto in read la seriale bluetooth e fin tutto bene, ora pero mi ritrovo con due dati che vaggiano entrambi tra 0 e 255 e devo dividerli per dire all arduino questo e l accelleratore e questo lo sterzo.
Ho provato a mettere un carattere di inizio un ‘#’ e poi un ciclo for che mette tutto il resto in un array ma se provo poi sul monitor seriale a leggere quello che mi trovo nell array e sempre uno 0 in tutte le posizioni.
Adesso non il pc a portata di mano ma piu o meno il codice che ho provato è cosi.

int memoria [3];
byte a;
byte s;


void loop
{
while (bt.available ()>0)
{a=bt.read();
  If (a=='#')
      {for (s=0 ; s <3 ; s++)
             {memoria [s]=bt.read ();
               Serial.println (memoria [1]);}}}}

L ho scritto adesso a mano al volo qualche parte è mancante tipo il setup ma piu o meno e per far capire la cavolo che ho provato :zipper_mouth_face:
Qualche consiglio per sbloccarmi?
Grazie!

Cosa succede se entri nel primo if con bt.available() che ritorna 1?

SukkoPera:
Cosa succede se entri nel primo if con bt.available() che ritorna 1?

Non ho capito cosa intendi.
Dici di mettere un if al posto del while?

Scusa, volevo dire nel primo while. Insomma, se entri nel while con bt.available() che ritorna 1, cosa succede? La prima bt.read() ti dirà che c’è un #, ma le successive? Chi ti garantisce che siano già arrivati altri 3 caratteri?

Inoltre va ricordato che il '#' corrisponde al valore 35, per cui non è un modo sicuro per sincronizzare i dati.

Salve,
Premetto che non ho mai usato il bluetooth, ma soltanto immaginando mi verrebbe da fare una cosa di questo tipo:
Date due variabili chat, su cui metto il byte letto da ciascuna delle seriali e una variabile unsigned long che mi fa da timer, e tutte le costanti date dal codice (inizi, terminatori, separazioni ecc.), e lavorando con numeri in cifre
Inizio le seriali
Ciascuna delle due variabili diventa uguale al letto sulla propria seriale
Se una di esse é maggiore a 0 gestisco il contenuto e azxero il timer
Leggo la seriale in una while (millis()-timer<certo tempo) e gestiscono i dati mano mano che arrivano. Ogni volta che leggo qualcosa azzero il contatore, che riparte da capo.
Così facendo ho la certezza che leggo sempre qualcosa, se poi rendo certo tempo sufficiente da garantire la ricezione di due caratteri consecutivi allora ho la certezza che leggo tutto quello che arriva, sul quale operò un dato alla volta.
Ora passiamo alla fase di operare.
Il primo dato che devo leggere é l’inizio, che mi da la seriale, e quindi la variabile, su cui operare. Variabile che azzerò
Dal secondo in poi devono essere tutte cifre (accettati anche 1 - e 1 , e il terminatore). Dopo aver controllato che le cifre siano cifre (if (isDigit (rx))) rendo la variabile uguale a essa*10+rx-‘0’. Per i meno, controllati in un else, la rendo uguale a meno se stessa, e per la virgola… É un po difficile ma si fa.
Quando trovo il terminatore operazione finita. Che faccio? Da decidere (svuoto o non svuoto la seriale?)
E se non lo trovo?
E se trovo cose non accettate?
Sono tutti casi da considerare, ma credo si possa fare

SukkoPera:
Scusa, volevo dire nel primo while. Insomma, se entri nel while con bt.available() che ritorna 1, cosa succede? La prima bt.read() ti dirà che c'è un #, ma le successive? Chi ti garantisce che siano già arrivati altri 3 caratteri?

ok allora se entro nel while con bt.available che ritorna 1 esegue la sua istruzione e cioe mette nella variabile x in contenuto della seriale bt poi leggo il # e poi non so risponderti :stuck_out_tongue: ed è qui che mi blocco :stuck_out_tongue:

Claudio_FF:
Inoltre va ricordato che il '#' corrisponde al valore 35, per cui non è un modo sicuro per sincronizzare i dati.

meglio un carattere con valore oltre il 255? questo intendi?

Io_me:
Salve,
Premetto che non ho mai usato il bluetooth, ma soltanto immaginando mi verrebbe da fare una cosa di questo tipo:
Date due variabili chat, s............................

mi sono un pochino confuso sono sincero

vale88:
meglio un carattere con valore oltre il 255? questo intendi?

ho detto una fesseria pardon!

Io in generale uso un altro approccio in questi casi. Ogni volta che c'è un carattere in arrivo, lo accodo in un buffer. Se il carattere appena arrivato è un terminatore definito precedentemente (di solito uso un semplice '\n', ovvero un "a capo"), invece di accodarlo vedo cosa c'è nel buffer e lo processo.

Ti consiglio di provare a fare qualcosa di simile.

ok faccio qualche prova inatanto vediamo se ho capito.
dal trasmettitore invio una cosa del genere:

 BT.write(valore1);
  BT.write(valore2);
  BT.write('/n')

è esatto?
e poi me lo smazzo nel ricevente magari usando una for che mi mette tutto in un array finche non leggo il /n e allora esco dal ciclo for ed eseguo il resto
ci sono piu o meno?

aggiornamento:
ho provato in questo modo, cosi sulla ricevente leggo i valori dei potenziometri e il '/n? che equivale a 110 e siccome i potenziometri mi viaggiano da 0 a 255 mi porteranno problemi quando passeranno per 110.
quindi o mappo i potwenziometri da 0 a 10 per sempio o uso uno 0 o un 255 come terminatore sempre ovviamente eliminandolo dai potenziometri

Intanto è \n, non /n.

Poi boh, più o meno. Io uso una funzione tipo questa:

char *readSerialString () {
   static char buf[100];
   static int i = 0;

   char *ret = NULL;

   while (Serial.available ()) {
       char c = Serial.read ();
       switch (c) {
         case '\r':
           // Ignore
           break;
         case '\n':
           // End of string, process
           buf[i] = '\0';
           ret = buf;
           i = 0;
           break;
         case -1:
           // No char available to read
           break;
         default:
           // Got new char, append
           buf[i++] = c;
           break;
     }
   }

   return ret;
 }

void loop () {
 char *str;

 if ((str = readSerialString ())) {
     Serial.print ("Received: \"");
     Serial.print (str);
     Serial.println ("\"");
   }
}

Per evitare problemi puoi sostituire la write con una print. Essa é simile alla prima, ma divide i numeri in cifre, che hanno codice ASCII da '0' a '0'+9.
In lettura devi leggerle e ottenere il loro valore sottraendo al loro codice quello di 0. I codici non devi ricordarteli a memoria, basta scrivere -'0' e fa lui. Il problema é che così facendo ti serve un terminatore intermedio, visto che il numero di cifre non é costante

Io_me:
Il problema é che così facendo ti serve un terminatore intermedio, visto che il numero di cifre non é costante

Basta renderla costante aggiungendo gli zeri iniziali.
Potrebbe anche trasmettere in rappresentazione esadecimale così risparmia due caratteri.

Non so però quanto siano affidabili quei ricetrasmettitori e se serva aggiungere anche un controllo errore.

Personalmente oltre a un terminatore preferisco agganciarmi in ricezione a un preambolo iniziale, in modo da discriminare chiaramente l'inizio della trasmissione da eventuali "byte vaganti" che riempirebbero solo il buffer di cose inutili (e difficili da processare).

Ma ci sono altri modi, ad esempio lavorando a livello di bit, con soli quattro byte si riuscirebbe a trasmettere sync univoco, due byte dati, un byte CRC8, e avanzerebbero ancora quattro bit inutilizzati.

Se non ho capito male comunque devo usare la string giusto?
Se è cosi me la devo studiare un po che non la conosco ancora.

vale88:
Se non ho capito male comunque devo usare la string giusto?
Se è cosi me la devo studiare un po che non la conosco ancora.

Mi raccomando "string" con la 's' minuscola, NON la classe "String" con la 'S' maiuscola ! :wink:

Guglielmo

vale88:
Se non ho capito male comunque devo usare la string giusto?

Non è necessario.

Quelli che arrivano in ogni caso sono byte.

Se viene trasmessa una stringa, vengono trasmessi i byte che rappresentano i caratteri secondo il codice ASCII, quindi i caratteri ‘0’ ‘1’ ‘2’ ecc verranno trasmessi come valori 48 49 50 ecc.

Sta a te decidere come usarli.

Ad esempio se viene trasmessa la stringa “#005132*” basta togliere 48 dai byte che rappresentano le cifre per avere i valori 0…9 corrispondenti a ogni cifra, e con 100*a + 10*b + c otteniamo il nostro valore 0…255 (senza usare funzioni di stringa e altre conversioni).

Una stringa come quella, con lunghezza e formato fissi, si trasmette semplicemente con:

Serial.write('#');
if(v1 < 10)  Serial.write('0');
if(v1 < 100) Serial.write('0');
Serial.print(v1);
if(v2 < 10)  Serial.write('0');
if(v2 < 100) Serial.write('0');
Serial.print(v2);
Serial.write('*');

Ok l invio ci anche siamo, il problema che mi trovo è dividere i due dati al ricevimento sull ricevitore.

Devo distinguere i due dati dividerli quindi metterli in una string e in base alla posizione se 0 o 1 so che è per l accelleratore o per lo sterzo pero dovrei mettere un divisorio tra l invio dei due valori oppure ricevere tutto un blocco e dividere pezzo pezzo.
Sto ragionando bene o mi sto solo complicando la vita?

Ognuno qua pare avere una soluzione diversa, quindi anch'io proseguo per la mia strada. Io invierei due righe separate, visto che non è nemmeno detto che vadano inviate sempre insieme. Inoltre, per semplicità, invierei sempre caratteri ASCII. Ad esempio per impostare la velocità a 40 (valore a caso), manderei:

V:40\n

Per sterzare a 10:

S:10\n

Riprendendo la funzione che ti ho suggerito prima, basterebbe fare:

// Variabili globali contenenti gli attuali valori di velocità e sterzo
int vel = 0, sterzo = 0;

void loop () {
  char *str;

  if ((str = readSerialString ())) {
     Serial.print ("Received: \"");
     Serial.print (str);
     Serial.println ("\"");

     switch (str[0]) {
       case 'V':
         vel = atoi (str + 2);
         break;
       case 'S':
         sterzo = atoi (str + 2);
         break;
       default:
         // Comando non riconosciuto
         break;
   }
}

Così hai anche un minimo di validazione dei comandi, ed il tutto è estremamente semplice da gestire.