Help: ricevere una sequenza di bytes

salve a tutti nel forum!
Un dispositivo commerciale trasmette dei dati byte a byte senza particolari delimitatori e mescolati a caratteri del tutto casuali come segue:
xxxxxxxxxxxABC[BYTE0][BYTE1][BYTE2][BYTE3]xxxxxxxDEF[YTE0][BYTE1][BYTE2][BYTE3]xxxxxxxxx
X rappresenta un numero casuale di bytes non costante.
In pratica la stringa ABC rappresenta l'identificativo per catturare i 4 bytes che seguono e così via.
Non ci sono caratteri particolari per la delimitazione e nessun CR o New_Line
Come posso fare per "estrarre" gli otto bytes dei dati ?
Grazie anticipatamente .
Saluti

Ciao!Qualche parola in più non guastava :slight_smile: Presumo si parli di seriale!
Presumo anche che la trasmissione avvenga in momenti specifici, ossia questo dispositivo non trasmette continuamente ma solo in momenti particolari, anche perché poi se arduino leggesse continuamente da seriale non potrebbe fare altro!

Come si potrebbe fare? Usando macchine a stati finiti per individuare la sequenza di caratteri che precedono i 4 byte validi.

  1. Se ci sono byte sulla seriale li leggo a uno a uno
    2)Creo una macchina a stati finiti "esempio sequenza ABC"
    Ricevo il carattere A passo allo stato 1, il carattere successivo è B passo allo stato 2, il terzo carattere è
    C passo allo stato 3.

3)Sono allo stato 3, ho ricevuto la sequenza che indica l'inizio dei byte, faccio 4 Serial.read() e leggo i byte
validi.


Adesso implementare la cosa comporterà le sue difficoltà ma la logica è questa, tu hai due sequenze diverse di caratteri, quindi dovrai implementare due macchine a stati finiti una per ogni sequenza, e leggere da seriale solo quando sono disponibili byte, quindi usare Serial.available().

Grazie per aver risposto.
Si, effettivamente non ho dato tutte le informazioni.
Si tratta di una uscita UART del dispositivo. Ho inviato tale uscita al monitor di di Arduino IDE tramite adattatore USB e leggo continuamente dei caratteri "strani " senza senso tra i quali compaiono i dati che mi interessano preceduti da ABC. Il suggerimento è utilissimo : controllo il Serial.available, se >0 faccio un Serial.read caricando la lettura in una variabile dichiarata CHAR . Se questa è uguale ad 'A' proseguo: come Per favore ? Ancora con Serial.available e poi Serial.read ? Per ora non funziona...
Grazie nuovamente.
Saluti

Ciao! Io avevo pensato di usare degli if e else if, vediamo di farti qualche esempio che sicuramente non funziona al primo colpo, ma andrà corretto e modificato "Non sono un genio, io provo sbaglio e porto delle correzioni" :wink: :slight_smile:

Esempio:

byte macchina1=0;
byte macchina2=0;
byte mybyte[4];
byte i=0;
void loop(){
 

    byte dato=0;
    if (Serial.available() > 0) {// Se ci sono byte sulla seriale leggo byte per byte
   
       //Non ho bisogno di un while perché non c'è terminatore, se ci sono byte leggo e il loop() ripete
       dato = Serial.read();
       
       // Prima macchina a stati finiti, se riceve la sequenza ABC arriva allo stato 3, se c'è un carattere
       // diverso else torna allo stato zero
       if(dato=='A' && macchina1==0){

           macchina1=1;
       }else if(dato=='B' && macchina1==1){
         
            macchina1=2;
       }else if(dato=='C' && macchina1==2){

           macchina1=3;

       }else{

            macchina1=0;
       }


       /********************************************/

      //Seconda macchina a stati finiti

      if(dato=='D' && macchina2==0){

           macchina2=1;
       }else if(dato=='E' && macchina2==1){
         
            macchina2=2;
       }else if(dato=='F' && macchina2==2){

           macchina2=3;

       }else{

            macchina2=0;
       }
    }

     /* Se ho ricevuto la giusta sequenza di macchina1 */

    if(macchina1==3){

         i=0;
         while(i<4){
             while(!Serial.available()); // Attende che ci sia un byte disponibile
             mybyte[i]=Serial.read();
             i++;

         }
         
         macchina1=0; // Porto la macchina1 allo stato zero

    }
}

modifica avevo messo if al posto di else if

Mi viene in mente che tu hai detto che ti compaiono caratteri strani, che se tu non leggi solo quando effettivamente ci sono byte, poi compaiono caratteri strani. Nel senso la comunicazione ha una sua velocità che è inferiore alla velocità di arduino, per cui se non dici ad arduino di leggere solo se ci sono byte, lui legge più veloce della trasmissione e per cui vengono fuori caratteri strani.

Per ricevere da arduino devi sempre mettere un if (Serial.available() > 0) prima della Serial.read();

Secondo me si può fare anche con una FSM sola.

ambrofsm.png

Il tutto funziona ovviamente se e solo se tra i "caratteri strani" (probabilmente sono byte non stampabili) non compaiono mai le sequenze di inizio ABC o DEF.

ambrofsm.png

sì, beh, "ovviamente" la sequenza di riconoscimento deve essere univoca, nel senso che non deve comparire indebitamente, insomma, mi accorgo che sto scrivendo un fracco di banalità, scusatemi
invece per lo OP: prova a dare uno sguardo indietro, ci sono post e post che fanno quallo del quale hai bisogno
mi viene in mente, solo tra quelli ai quali ho partecipato io
uno che cercava di decodificare il protocollo LINBUS
un insieme di funzioni (non una libreria) per messaggiare con Telegram (guarda la parte di ricezione e riconoscimento messaggi Json)
una mia discussione dove sviluppavo un "setaccio universale", discussione durante la quale il mio socio mi ha fatto anche notare un problema non banale in prima battuta, naturalmente poi risolto
di esempi dai quali trarre ispirazione c'è ne è un mare....

Grazie a tutti per le risposte.
ho utilizzato il prg inviatomi ma non funziona .
Ho scritto un breve programma per catturare solo ABC ma come risultato ho solo la cattura di "A".
Non so se ho recepito correttamente le vostre indicazioni per cui allego lo sketch:
Grazi nuovamente
Ciao

#include <SoftwareSerial.h>
SoftwareSerial mySerial(2, 3, true);

void setup() {

 //--- Open serial communications and wait for port to open:
 Serial.begin(115200);
 delay (100);

 Serial.println("Connesso a 115200 baud sulla seriale standard USB");

 //--- Set the data rate for the SoftwareSerial port
 mySerial.begin(9600);
 Serial.println("Connesso a 9600 :mySerial ");
 Serial.println();
}

void loop() {
 char rx_byte     = 0x00;
 
  if (mySerial.available() > 0) {
   rx_byte = mySerial.read();
    if(rx_byte=='A'){
     Serial.println(rx_byte);
     if (mySerial.available() > 0) {
       rx_byte = mySerial.read();
       if(rx_byte=='B'){
         Serial.println(rx_byte);
        if (mySerial.available() > 0) {
          rx_byte = mySerial.read();
          if(rx_byte=='C'){
          Serial.println(rx_byte);
          Serial.print( "*************  trovato  ABC ***********");
               }
             }
           }
        }
       }
    }
   }
// }
//}

>Ambro50: ti ricordo che in conformità al regolamento, punto 7, devi editare il tuo post qui sopra (quindi NON scrivendo un nuovo post, ma utilizzando il bottone More -> Modify che si trova in basso a destra del tuo post) e racchiudere il codice all'interno dei tag CODE (... sono quelli che in edit inserisce il bottone con icona fatta così: </>, tutto a sinistra).

In pratica, tutto il tuo codice dovrà trovarsi racchiuso tra due tag: [code] _il _tuo_ codice_ [/code] così da non venire interpretato e non dare adito alla formazione di caratteri indesiderati o cattiva formattazione del testo. Grazie.

Guglielmo

Hai seguito una strada diversa da quella che ti avevo indicato! Avresti potuto cercare di capire l esempio, mettere qualche serial.prinln nei vari stati per vedere se passava dagli Stati ,0;1;2;3, e trovare errori e bug.

Ambro50:
Ho scritto un breve programma per catturare solo ABC ma come risultato ho solo la cattura di "A".

Il problema è che il tuo codice è troppo veloce rispetto all'arrivo dei byte seriali. Funzionerebbe solo se i tre caratteri fossero già arrivati e presenti nel buffer di ricezione. In pratica all'arrivo di 'A' leggi correttamente il byte ma controlli subito se c'è qualcos'altro disponibile con available. Se il carattere successivo non ha fatto ancora in tempo ad arrivare available restituisce zero e la procedura termina.... ricominciando dall'inizio.

@torn24,
si ho modificato lo sketch ed ho anche messo dei print ma in pratica supera solamente il primo controllo e basta.
Ho provato in diversi modi e dopo ho provato a scriverne uno per ricevere solamente ABC.... ma anche fatto in modo molto semplice non va.

@ Claudio_ff: Come posso fare? mi potresti dare dei consigli ?
Grazie a tutti
Ciao

Scusa la domanda:
Ma è lo stesso problema per il quale all'inizio del mese ti ho detto di cercare tra i miei post?
Vabbe' vedremo come andrà a finire

@Standardoil:

ho cercato ma non riesco a trovare quello che dici.
Non ho messo alcun post all'inizio del mese.

hai qualche dritta da darmi?

ciao

Torn24 / Claudi_FF:
Potreste per favore darmi una mano a catturare solamente ABC e stampare "ricevuto ABC" tanto per cominciare con un prg molto semplice. Penso che il primo problema sia quello di verificare la corretta cattura di ABC.
Grazie
Ciao

Se ho capito bene e il problema consiste nell'aspettare la sequenza "ABC", leggere 4 byte, aspettare la sequenza "DEF" e leggere altri 4 byte, con "ABC" che precede sempre "DEF", questo programma dovrebbe funzionare (ma non e' testato).

// aspetta finche' un byte e' disponibile in mySerial e lo legge
byte blockingRead()
{
 while(mySerial.available() < 1)
 ;
 
 return(mySerial.read());
}

// scarta tutti i byte finche' non legge la sequenza sequence

#define WAIT_FOR_FIRST  1
#define WAIT_FOR_SECOND 2
#define WAIT_FOR_THIRD  3
 
void waitForSequence(const byte *sequence)
{
 byte b;
 int state = WAIT_FOR_FIRST;
 
 while(true)
 {
 b = blockingRead();
 
 if ((b == sequence[0]) && (state == WAIT_FOR_FIRST))
 {
 state = WAIT_FOR_SECOND;
 continue;
 } 

 if ((b == sequence[1]) && (state == WAIT_FOR_SECOND))
 {
 state = WAIT_FOR_THIRD;
 continue;
 } 

 if ((b == sequence[2]) && (state == WAIT_FOR_THIRD)) 
 return;
 
 state = (b == sequence[0]) ? WAIT_FOR_SECOND : WAIT_FOR_FIRST; 
 } 
}

// aspetta e legge 4 byte in myBytes
void read4Bytes(byte *myBytes)
{
 for(int i=0; i<4; i++)
 myBytes[i] = blockingRead();
}

// aspetta la sequenza di start assegnata e poi legge 4 byte e li salva in myBytes 
void readStartSequenceAndBytes(const byte *startSequence, byte *myBytes)
{
 waitForSequence(startSequence);
 
 read4Bytes(myBytes);
} 
 
void loop()
{
 byte myBytes[8];
 
 // aspetta la sequenza "ABC" e legge i 4 byte successivi
 readStartSequenceAndBytes((const byte *)"ABC",myBytes);
 
 // aspetta la sequenza "DEF" e legge i 4 byte successivi
 readStartSequenceAndBytes((const byte *)"DEF",myBytes+4);
 
 // fa qualcosa con gli 8 byte 
}

Ambro50:
Potreste per favore darmi una mano a catturare solamente ABC e stampare "ricevuto ABC"

if (Serial.available())
{
    uint8_t rx = Serial.read();

    switch (fase)
    {
        case 0:
            if ('A' == rx) { fase = 1; }
        break;

        case 1:
            if ('B' == rx) { fase = 2; }
            else           { fase = 0; }
        break;

        case 2:
            if ('C' == rx) { Serial.print("Ricevuto ABC"); }
            fase = 0;
        break;
    }
}

E se non piace lo switch...

if (Serial.available())
{
    uint8_t rx = Serial.read();

    if      ((0 == fase) && ('A' == rx)) { fase = 1; }
    else if ((1 == fase) && ('B' == rx)) { fase = 2; }
    else if (1 == fase)                  { fase = 0; }
    else if (2 == fase)
    { 
        if ('C' == rx) { Serial.print("Ricevuto ABC"); }
        fase = 0;
    }
}

Ambro50:
@Standardoil:

ho cercato ma non riesco a trovare quello che dici.
Non ho messo alcun post all'inizio del mese.

hai qualche dritta da darmi?

ciao

https://forum.arduino.cc/index.php?topic=613336.msg4157235#msg4157235
Come vedi il primo di maggio è proprio inizio del mese, non tentare di farmi passare per fesso, grazie
tutte le dritte che avevo te le ho già date all'ora
se in quasi un mese non sei riuscito a trovare un post dove sapevi esserci "esattamente" la soluzione al tuo problema, io altro non posso fare

Ottima soluzione quella proposta da VLC0617: funziona molto bene. Congratulazioni per l'assistenza.
Il prg è un poco duro da comprendere per me : dovrò studiarlo nei dettagli.
Devo provare il prg postato da Claudio_FF, cosa che potrò fare domani.
Ringrazio tutti nel forum per la collaborazione dimostrata.
Saluti,
Ciao

Il programma proposto "che ti piace" è organizzato in funzioni e ti può apparire una soluzione diversa!
Se avessi capito come usare una macchina a stati finiti, avresti capito che tutti gli esempi, tutte le soluzioni, si basano sulle stesse cose. Infatti si possono considerare implementazioni diverse di uno stesso algoritmo " algoritmo=passi finiti per giungere a una soluzione".
Tutti gli esempi sono implementazioni di una macchina a stati finiti per riconoscere una sequenza

La differenza del programma di vlc0617, invece di fare tutto il necessario nel loop(), si è creato una funzione per ogni scopo.