ricezione e memorizzazione seriale

buongiorno a tutti.
ho bisogno di una mano per un progetto che sto facendo.
comandare 3 servo indipendenti ma per quello spero di non trovare problemi piu avanti...ora ho bisogno riguardo la ricezione seriale dei dati, poichè il controllo dei servo lo effettuo da un joystick collegato al pc...
tramite un programma che ho gia svolto converto tutti gli assi di rotazione dei pad in decimali e li invio su seriale:
ho a diposizione 3 assi differenti:
asse x, asse y, asse z ogni asse un servo..
i range sono tra 0 e 1024 per ogni asse, anticipati da una lettera di distinzione e una di fine:
ad esempio :
x302f posiziono il servo(x) a posizione 302 (proporzionato ai gradi)
y1003f
z9f
la mia domanda è: come faccio nel modo più semplice a leggere i dati e memorizzarli in 3 variabili differenti estrapolando solamente il numero (salvarli come integer)? ho bisogno di un aiuto abbastanza approfondito
grazie in anticipo!

L'argomento è stato affrontanto tante volte e trovi nel forum un sacco di esempi di codice e di risoluzione di problematiche varie difficili da affrontare in un unico post.
Ti posso solo suggerire che la trasmissione seriale avviene per singoli byte, quindi se spedisci la stringa "x302f" l'Arduino riceverà nell'ordine i caratteri "x", "3", "0", "2" ed "f". Sta quindi a te leggere i dati, ricombinando i valori ricevuti in numeri e comandi.

si lo so ho cercato ovunque in merito...ho trovato degli spezzoni di codice che immagazzinano ogni byte ricevuto dalla seriale in un elemento dell'array...quello che pero non riesco a trovare è come faccio se in un array di x elementi a trasformarlo in un intero con unità decine centinaia ogni singolo byte dell'array? se array X = [x,5,7,2,f] come trasformo 572 in un integer?

Se sai che la stringa da ricevere è sempre composta così: lettera-numero-numero-numero-lettera, stai in ascolto sulla seriale. Appena leggi una trasmissione, raccogli 5 byte. Estrapoli il 1° ed il 5° per i tuoi scopi. Poi puoi fare un minchiodice (ti piace il neologismo? XD ) come questo

int valore;
valore = (int(stringa[1])*100)+(int(stringa[])*10)+int(stringa[3])

Non so se funziona però ad occhio e croce perché no? XD

giusto...dovrebbe andare in teoria...purtroppo dovrò fare anche un algoritmo automatico xke la dimensione degli array cambia sempre..... (puo essere x3f ma anche x1020f)..purtroppo non ho modo di tenere gli 0 al posto delle decine e centinaia prima della trasmissione..

inoltre ero intenzionato a inviare tutti gli assi e i successivi tasti in un'unica stringa esempio ( x200y145z794p1......f finale)
e più gestibile giusto?seziono poi tutti i dati..

Perché allora non mandare 2 byte con il valore del movimento codificato in byte_basso, byte_alto?
In questa maniera hai una stringa di formato fisso ed anche facilmente convertibile (basta rimettere insieme i 2 byte che ottieni l'intero di partenza. Così puoi mandare valori da 0 a 65535.

in che senso?non ti sto seguendo...fai un esempio pratico.....i byte che invio rispecchiano gia la posizione che andrà ad assumere il servo...non capisco come intendi tu..

Devi mandare un valore che può arrivare, vedo, anche a 1000?
Bene, prendiamo il caso di 1000: invece di inviare 1-0-0-0 invii sulla seriale lowByte(1000) e highByte(1000), ossia frammenti il numero nelle sue componenti. In questo modo spedisci il numero frazionato in 2 byte e non in 4 caratteri, risparmi anche.
Alla ricezione ricomponi con (int16)numero=(highbyte<<8) OR lowbyte e riottieni il valore originale senza dover stare a convertire fra singoli caratteri e numeri. Inoltre hai una trasmissione fissa di 2 byte, sempre.
(pseudodice)

Per la conversione da stringa a int puoi anche utilizzare atoi():
http://www.cplusplus.com/reference/clibrary/cstdlib/atoi/

allora ragazzi ho fatto in questo modo: sono riuscito ad avere una trasmissione fissa di 4 byte per asse da pc verso seriale e invio i dati in una stringa unica di 13 byte (il primo è carattere di riconoscimento).

il programma non va..trasmetto ma la posizione dei servo non cambia(rimane fissa al minimo)...penso che ci sia un problema nella funzione ke mi ha incidato leo sopra:

assex = (int(messaggio[1])*1000)+(int(messaggio[2])*100)+(int(messaggio[3])*10) +int(messaggio[4]);

analizzando con l'oscilloscopio le uscite dei servo sono al minimo senza dati, e se inserisco la posizione con delle costanti cambia...quindi direi che quella parte funziona...

riporto il codice completo per vedere se ho commesso magari altri errori....
per ora non mi interessa se si puo risolvere con meno righe di codice o altro...voglio solo vedere che funziona, poi magari passero a migliorarlo...

#include <Servo.h>
int indexByteMessaggio;
byte inByte;
byte messaggio[] = {
0,0,0,0,0,0,0,0,0,0,0,0,0};

Servo servo1;
Servo servo2;
Servo servo3;
int posservo1;
int posservo2;
int posservo3;
int assex;
int assey;
int assez;
unsigned long time;
unsigned long servotime1;
unsigned long servotime2;
unsigned long servotime3;
void setup(){
servo1.attach(9);
servo2.attach(10);
servo3.attach(11);
Serial.begin(115200);
pinMode(ledpin,OUTPUT);
time=millis();
servotime1=millis();
servotime2=millis();
servotime3=millis();
assex = 0;
assey = 0;
assez = 0;
}
void loop (){
time=millis();
if (Serial.available() > 0) {
inByte = Serial.read();
if (inByte=='a'){
indexByteMessaggio=0;
}
messaggio[indexByteMessaggio] = inByte;
indexByteMessaggio = indexByteMessaggio + 1;
}

assex = (int(messaggio[1])*1000)+(int(messaggio[2])*100) +(int(messaggio[3])*10) +int(messaggio[4]);
assey = (int(messaggio[5])*1000)+(int(messaggio[6])*100) +(int(messaggio[7])*10) +int(messaggio[8]);
assez = (int(messaggio[9])*1000)+(int(messaggio[10])*100)+(int(messaggio[11])*10) +int(messaggio[12]);
if(time>servotime1+15){
posservo1= assex;
posservo1=map(posservo1,0,1023,0,179);
servo1.write(posservo1);
servotime1=millis();
}
if(time>servotime2+15){
posservo2= assey;
posservo2=map(posservo2,0,1023,0,179);
servo2.write(posservo2);
servotime2=millis();
}
if(time>servotime3+15){
posservo3= assez;
posservo3=map(posservo3,0,1023,0,179);
servo3.write(posservo3);
servotime3=millis();
}
}

Scusa ma con int di un char :int(messaggio[1]) non ti restituisce il valore in ascii di quel carattere? Io userei atoi che è più semplice.

si ma non riesco a capire come utilizzzarla questa istruzione...
devo lo stesso memorizzare ogni byte in ogni cella dell'array e poi utilizzare atoi o posso già suddividere la stringa in 3 stringhe da 4 byte ciascuna e convertirle dopo eliminando prima il carattere iniziale?
scusate ma sono un nubbio quando utilizzo istruzioni mai usate....ho iniziato da poco a programmare con arduino quindi trovo ancora un po di problemi se le cose non mi sono molto chiare...
grazie per la vostra pazienza...

Uhm.. ma prima di tutto forse dovresti seguire una piccola guida di programmazione, perché se ti mancano le basi per poter scrivere gli sketch, come fai per programmare i micro? :sweat_smile:
@ypkdani:
difatti lo avevo detto che era "michiodice" :wink:

Una volta verificato che il primo carattere e corretto allora invii la sottostringa contenente solo i numeri da convertire.

quindi salvo tutti i dati in una stringa intera giusto?
poi utilizzo una substring?
Str2=Str1.subString(1,4)
Str3=Str1.subString(5,8)
Str4=Str1.subString(9,12)

int a = atoi(Str2);
int b = atoi(Str3);
int c = atoi(Str4);
corretto come procedimento?

Allora per prima cosa nel software hai:

if (Serial.available() > 0) {
    inByte = Serial.read();
    if (inByte=='a'){
      indexByteMessaggio=0;
    }
    messaggio[indexByteMessaggio] = inByte;
    indexByteMessaggio = indexByteMessaggio + 1;
  }

con questo codice però acquisisci solo un carattere alla volta, mettici un while al posto dell'if.
Per la conversione se come ho capito ti arriva una stringa del tipo "x456f" allora potresti fare:

int pos=0;
if(messaggio[0]=='x'){
  int leng=strlen(messaggio);
  char buffer[10]="";
  for(int i=1; i<leng; i++ ){
    buffer[i]=messaggio[i];
  }
  pos=atoi(buffer);
}

ecco il nuovo codice che ho trascritto sulla tua base:

int pos=0;
int indexByteMessaggio;
byte inByte;
char messaggio[13]="";
char buffer[13]="";

void setup(){
Serial.begin(115200);
}

void loop()
{
  while (Serial.available() > 0) {
    inByte = Serial.read();
    if (inByte=='a'){
      indexByteMessaggio=0;
    }
    messaggio[indexByteMessaggio] = inByte;
    indexByteMessaggio = indexByteMessaggio + 1;
  }
  
  if(messaggio[0]=='a'){
  int leng=strlen(messaggio);
  
  for(int i=1; i<leng; i++ ){
    buffer[i]=messaggio[i];
  }
  pos=atoi(buffer);
  
}
Serial.println(messaggio);
delay(500);
}

col serial monitor controllo il dato che è stato letto correttamente, infatti se visualizzo messaggio il dato è corretto..
se invece vado a stampare "pos" mi rimane sempre a 0..ci deve essere un problema nella conversione...
io invio una stringa di questo tipo: a005403471004 0054 assex 0347 assey 1004 assez
col codice che mi hai dato tu converto tutto assieme in un numero...dovrei prima spezzarlo in 3 stringhe e poi ripertere la conversione per rurre e 3 giusto?
ma se già la conversione totale non funziona che faccio?
una volta che riesco a memorizzare sti 3 assi non ho piu problemi dopo!!è solo questa parte che mi complica!!

se riesco a fare sta benedetta conversione vi meritate un regalo :wink: :wink:

Non ho con me arduino quindi non posso provarlo, prova a vedere se stampando buffer ti compare la parte corretta cioè solo i numeri. No non devi inviare un numero alla volta, si arrangia tutto la funzione atoi.

Prova a fare:

char buffer[13]="1234";
pos=atoi(buffer);
Serial.println(buffer);
Serial.println(pos);

dovrebbe funzionare

avevo provato anche a visualizzare buffer ma risultava vuoto...ora provo il codice che mi hai scritto e ti dico

ok confermo che :

char buffer[13]="1234";
pos=atoi(buffer);
Serial.println(buffer);
Serial.println(pos);

funziona.

quindi ci sarà qualcosa che non va in questo spezzone:

if(messaggio[0]=='a'){
  int leng=strlen(messaggio);
  
  for(int i=1; i<leng; i++ ){
    buffer[i]=messaggio[i];
  }
  pos=atoi(buffer);