Caratterizzare e identificare trasmissione seriale

salve a tutti,
sto inviando via seriale una variabile numerica long,che dall'altra parte viene riconosciuta e calcolata, ora vorrei poter inviare due variabili e poterle riconoscere dall'altra parte per poterle gestire in modo diverso... non so se sono stato chiaro.. cmq

tx

  Serial.write(lowByte(DATO1));  // trasmette il byte meno significativo
  Serial.write(highByte(DATO1)); // trasmette il byte più significativo

rx

  if (Serial.available() > 1)
  {
    DATO1 = Serial.read();          // riceve byte basso
    DATO1+= (int)Serial.read()
    Serial.println(DATO1);
}

ora vorrei poter inviare a mio piacimento DATO1 o DATO2 e dall'altra parte riconoscere se è arrivato DATO1 o DATO2 e metterli in calcoli diversi

DATO1 e DATO2 sono variabili long entrambe.

qualche idea?

grazie

ciao
ciao

no me ne vogliate se ho chiesto una cosa troppo cretina…

per ora studio questo:

#include <NewSoftSerial.h>
NewSoftSerial my(3,2);

char val;
String string="";
String a="";
String b="";
String c="";
int a_int,b_int,c_int;

void setup(){
  Serial.begin(9600);
  my.begin(9600);
}

void loop(){
  
  
  
  if(my.available()>0)
  {
    a=b=c=string="";
    
    val=my.read();
    
    while(val!=';'){
     if(my.available()>0)
     {
       string+=val;
       val=my.read();     
     }
    }
    
    Serial.println(string);
      
    for(int i=0;i<string.length();i++){
      if(string.substring(i,i+1).equals("a"))
      {
        i++;
        while(!string.substring(i,i+1).equals("b")){ 
          a+=string.substring(i,i+1); 
          i++;
        }
      }
      if(string.substring(i,i+1).equals("b"))
      {
        i++;
        while(!string.substring(i,i+1).equals("c")){ 
          b+=string.substring(i,i+1); 
          i++;
        }
      }
      if(string.substring(i,i+1).equals("c"))
      {
        i++;
        while(!string.substring(i,i+1).equals("")){ 
          c+=string.substring(i,i+1); 
          i++;
        }
      }
    }
    a_int=stoi(a);
    b_int=stoi(b);
    c_int=stoi(c);
    Serial.print(a_int);
    Serial.print("--");
    Serial.print(b_int);
    Serial.print("--");
    Serial.println(c_int);
    
   
    
  }
  
  

  
}


int stoi(String text)
{
  char temp[20];
  text.toCharArray(temp, 19);
  int x = atoi(temp);
  if (x == 0 && text != "0")
  {
    x = -1;
  }
  return x;
}
}

Vado bene?!?!

Puoi risolvere anche semplicemente inserendo un carattere speciale che separa nella trasmissione le due variabili e poi quando le ricevi, cerchi il carattere e separi le due parti :wink:

Guglielmo

Scorrendo il codice di cui sopra mi viene proprio voglia di una soluzione alternativa più facile....

Un linketto da cui scopiazzare??

Grazie mille

sto cercando di utilizzare String

questo il codice creato…

tx

#define BUTTON1 5 

int val = 0;
long cm, feet, inches, INCHES;
int X;
unsigned int durata;
char FT;
char MT;
char LUM;

void setup() {
  
//Inizializzo la porta Seriale
Serial.begin(9600);

pinMode(BUTTON1, INPUT); 

}

void loop() 
{

 val = digitalRead(BUTTON1);

durata = sonar.ping_median(10);


 if (val == LOW) {
        //Inizializzo la stringa
  String stringa="";
//Aggiungo alla stringa tutti i valori separati da virgole
    stringa += char(FT);
    stringa += char(',');
    stringa += unsigned int(durata);  //unsigned int durata
    stringa += char(',');
    stringa += int(X);
    stringa += char(',');
    stringa += char(LUM);

  //Stampo la stringa a Seriale
  Serial.println(stringa);
    }
  else {
            //Inizializzo la stringa
  String stringa="";
//Aggiungo alla stringa tutti i valori separati da virgole
    stringa += char(MT);
    stringa += char(',');
    stringa += unsigned int(durata);  //unsigned int durata
    stringa += char(',');
    stringa += int(X);
    stringa += char(',');
    stringa += char(LUM);

  //Stampo la stringa a Seriale
  Serial.println(stringa);
  }

RX

void setup() {
  // put your setup code here, to run once:

}

void loop() {
  // put your main code here, to run repeatedly:
  // startsWith() checks to see if a String starts with a particular substring:
  String stringa = Serial.read();          // riceve byte basso
  String stringa+= (int)Serial.read()<<8;  // riceve byte alto
  
  if (stringa.startsWith("FT")) {
    Serial.println("FT");
  }
    if (stringa.startsWith("MT")) {
    Serial.println("MT");
  }
}

questa è solo chiaramente una bozza …

nell’RX è giusto ricomporre la stringa come ho fatto?

sono sulla strada giusta?

se si, poi come faccio ad estrapolare i valori di “durata” e di “x” dato che “durata” è una variabile che ha un numero di cifre che varia??

graziee

Una variabile long è lunga quattro byte.
Si possono trasmettere i vari byte in sequenza decidendo a priori il byte order, cioè se si debba iniziare con quello più significativo o con quello meno. Per semplicità di ricezione trasmettiamo per primo quello più significativo (byte order big endian):

Serial.write(dato >> 24);       // trasm. byte piu` significativo
Serial.write(dato >> 16);
Serial.write(dato >> 8);
Serial.write(dato);             // trasm. byte meno significativo

In ricezione ricostruiamo il long scorrendo verso sinistra i vari byte in arrivo:

dato = Serial.read();                   // rx byte piu` significativo
dato = (dato << 8) | Serial.read();
dato = (dato << 8) | Serial.read();
dato = (dato << 8) | Serial.read();     // rx byte meno significativo

È sufficiente aggiungere un byte “selettore di tipo”, ad esempio il carattere ‘H’, prima di quelli del long per discriminare cosa sta arrivando:

Serial.write('H');              // carattere H selettore di tipo
Serial.write(dato >> 24);       // trasm. byte piu` significativo
Serial.write(dato >> 16);
Serial.write(dato >> 8);
Serial.write(dato);             // trasm. byte meno significativo

tipo = Serial.read();
dato = Serial.read();                   // rx byte piu` significativo
dato = (dato << 8) | Serial.read();
dato = (dato << 8) | Serial.read();
dato = (dato << 8) | Serial.read();     // rx byte meno significativo

Il problema principale, grave o ininfluente a seconda di come funziona il resto del codice, è la sincronizzazione tra dati trasmessi e dati che si attende il ricevitore:

  1. Il ricevente può iniziare a leggere casualmente da un punto qualsiasi i byte trasmessi
  2. Il carattere ‘H’ ha codice 72, anche qualsiasi altro byte del long può assumere il valore 72, quindi la discriminazione tra byte dato e byte selettore non è certa.

Per rendere univoci i bit appartenenti al long da quelli appartenenti al selettore ci sono varie strade, la più semplice mi sembra quella di trasmettere il long sotto forma di stringa di caratteri che lo rappresentano in esadecimale, preceduti dal selettore, e seguiti da uno zero binario di fine stringa.

Serial.write('H');
Serial.print(dato, HEX);
Serial.write(0);

In ricezione ci si può affidare ad una funzione che ricostruisce il valore di partenza da usare solo quando viene ricevuto un selettore valido, cioè quando si è certi di essere all’inizio dei dati trasmessi:

rx = Serial.read();
if (rx == 'H')       faQualcosaCon(rxHexLong());
else if (rx == 'T')  faQualcosaDiAltroCon(rxHexLong());
long rxHexLong() {
    long n = 0;
    while (byte rx = Serial.read()) {
        rx -= '0';
        if (rx > 9) rx -= 7;
        n = (n << 4) | rx;
    }
    return n;
}

cepics: premesso che è un argomento che è già stato trattato più volte sul forum e che quindi, se fai un po di ricerche, dovresti trovare vari thread che ne parlano, comunque … ti do un consiglio … poi vedi tu …

… fermati un attimo e dimentica la classe String.

Considera che NON sei su un PC dove c’è un sistema operativo ed un “garbage collector”, sei su una piccola MCU con solo 2KBytes di SRAM, dove devi fare tutto tu e dove usare la classe “String”, a causa dell’allocazione e riallocazione dinamica della memoria, porta quasi sempre … a grossi problemi e sicuri mal di testa !!! :smiling_imp:

Dedicaci un po’ di tempo ed impara ad usare le stringhe classiche del C … ovvero semplici array di char terminati dal carattere null (0x00) e le funzioni che trovi nella libreria standard (… che, oltretutto, è automaticamente inclusa dal IDE) AVR libc ed, in particolare, quanto è in <string.h>.

Riuscirai a scrive programmi in cui TU hai il controllo della memoria occupata, che saranno più ottimizzati, più veloci e più efficienti :slight_smile:

Guglielmo

grazie mille,

per Guglielmo:

ok lascio perdere String… grazie per le dritte anche se i link alle funzioni di c per me sono peggio dell’arabo ed in piu non hanno esempi che sono il mio pane…

per claudio:

tutto molto chiaro, ti ringrazio ancora, non ho capito solo l’ultima parte di codice che hai postato… questa:

long rxHexLong() {
    long n = 0;
    while (byte rx = Serial.read()) {
        rx -= '0';
        if (rx > 9) rx -= 7;
        n = (n << 4) | rx;
    }
    return n;
}

a che serve??

allora:
se ho capito bene questa è la funzione che mi ritira fuori il dato trasferito al netto di selettore e zero finale

long rxHexLong() {
    long n = 0;
    while (byte rx = Serial.read()) {
        rx -= '0';
        if (rx > 9) rx -= 7;
        n = (n << 4) | rx;
    }
    return n;
}

il dato mi sembra rimanga comunque in esadecimalee quindi lo dovrò poi in qualche modo convertire per essere visualizzato dal serial7seg (credo)

ho buttato giu questi:

tx

long duration, cm, INCHES, feet, inches;
long X;


void setup() {
   
   Serial.begin(9600);  // put your setup code here, to run once:

}

void loop() {
delay(500);
 Serial.write('C');
Serial.print(duration, HEX);
Serial.write(0);
Serial.println();
Serial.println();

Serial.write('F');
Serial.print(duration, HEX);
Serial.write(0);
Serial.println();
Serial.println();

Serial.write('X');
Serial.print(X, HEX);
Serial.write(0);
Serial.println();
Serial.println();
}

e nel monitor seriale vedo scorrere i tre dati in esadecimale con il selettore in testa…

rx

long duration, cm, INCHES, feet, inches;
long rx;
int x;
//long rxHexLong;

void setup() {                // put your setup code here, to run once:
   
Serial.begin(9600);
  
}

void loop() {     // put your main code here, to run repeatedly:
  
delay(1000);     // rallento per capire...
  

rx = Serial.read();
if (rx == 'F')       INCHES = (rxHexLong());
else if (rx == 'C')  cm = (rxHexLong());
else if (rx == 'X')  x = (rxHexLong());      // 

}


long rxHexLong() {
    long n = 0;
    while (byte rx = Serial.read()) {
        rx -= '0';
        if (rx > 9) rx -= 7;
        n = (n << 4) | rx;
    }
    return n;
}

e qui discrimina tra “F”,“C” e “X” e estrapola il dato netto.

domani proverò a trasmettere tra i due arduini ma nel frattempo vi chiedo…

ho aggiunto una variabile la “X”, è giusto come ho fatto?

il valore della “X” sarebbe potuto essere anche da int, ma l’ho messa come long per usare la stessa funzione per “pulire” tutti i dati… è giusto? o sto delirando?

grazie

cepics:
se ho capito bene questa è la funzione che mi ritira fuori il dato trasferito al netto di selettore e zero finale

Si, legge i caratteri ascii della stringa in arrivo (in quantità variabile da 1 a 8 ) e ricrea il valore long iniziale, fermandosi quando riceve lo zero (byte zero, non carattere '0' che vale 48).

il dato mi sembra rimanga comunque in esadecimalee quindi lo dovrò poi in qualche modo convertire per essere visualizzato dal serial7seg (credo)

L'esadecimale non esiste se non come rappresentazione visiva (esattamente come il decimale). In memoria (o trasmessi) non possono esserci valori esadecimali, ma solo binari (singoli byte). In questo caso non viene neppure trasmesso il binario originale del long (4 byte), ma fino a 8 byte con i valori ascii dei simboli delle cifre esa con cui si può rappresentare quel long (cioè una stringa di caratteri, per quello si vede in chiaro sul serial monitor).

Serial.write('C');

Serial.print(duration, HEX);
Serial.write(0);
Serial.println();
Serial.println();

Per fortuna i caratteri di fineriga trasmessi con println vengono ignorati dal programma ricevente, altrimenti entrerebbero nel calcolo del valore con disastri assicurati.

Inoltre non bisogna usare per selettore un carattere che fa parte delle possibili cifre esa, altrimenti si perde l'univocità del selettore rispetto a qualsiasi byte del dato e di nuovo si possono avere problemi di sincronizzazione (il ricevente non capisce se una 'F' o una 'C' fa parte del dato o è il selettore).

il valore della "X" sarebbe potuto essere anche da int, ma l'ho messa come long per usare la stessa funzione per "pulire" tutti i dati... è giusto? o sto delirando?

Non c'è niente da "pulire" se quello che si trasmette paro paro si riceve. I caratteri di fineriga trasmessi con println potrebbero causare problemi proprio qui, ma siccome non vengono riconosciuti come selettori, e passano dopo lo zero di finestringa, vengono semplicemente ignorati.